static volatile const char *BOINCrcsid="$Id$"; // The contents of this file are subject to the BOINC Public License // Version 1.0 (the "License"); you may not use this file except in // compliance with the License. You may obtain a copy of the License at // http://boinc.berkeley.edu/license_1.0.txt // // Software distributed under the License is distributed on an "AS IS" // basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the // License for the specific language governing rights and limitations // under the License. // // The Original Code is the Berkeley Open Infrastructure for Network Computing. // // The Initial Developer of the Original Code is the SETI@home project. // Portions created by the SETI@home project are Copyright (C) 2002 // University of California at Berkeley. All Rights Reserved. // // Contributor(s): // #include "cpp.h" #ifdef _WIN32 #include "boinc_win.h" #endif #include "util.h" #include "file_names.h" #include "client_state.h" #include "filesys.h" #include "client_msgs.h" #include "file_xfer.h" #include "parse.h" #include "error_numbers.h" using std::vector; FILE_XFER::FILE_XFER() { file_xfer_done = false; file_xfer_retval = 0; fip = NULL; strcpy(pathname, ""); strcpy(header, ""); file_size_query = false; } FILE_XFER::~FILE_XFER() { if (fip && fip->pers_file_xfer) { fip->pers_file_xfer->fxp = NULL; } } int FILE_XFER::init_download(FILE_INFO& file_info) { double f_size; is_upload = false; fip = &file_info; get_pathname(fip, pathname); if (file_size(pathname, f_size)) { f_size = 0; } bytes_xferred = f_size; return HTTP_OP::init_get(fip->get_current_url(is_upload), pathname, false, (int)f_size); } // for uploads, we need to build a header with xml_signature etc. // (see doc/upload.html) // Do this in memory. // int FILE_XFER::init_upload(FILE_INFO& file_info) { // If upload_offset < 0, we need to query the upload handler // for the offset information // TODO: give priority to unfinished upload if there are multiple choices // fip = &file_info; get_pathname(fip, pathname); is_upload = true; if (file_info.upload_offset < 0) { // NOTE: Should this be <=? bytes_xferred = 0; sprintf(header, "\n" " %d\n" " %d\n" " %s\n" "\n", BOINC_MAJOR_VERSION, BOINC_MINOR_VERSION, file_info.name ); file_size_query = true; return HTTP_OP::init_post2(fip->get_current_url(is_upload), header, NULL, 0); } else { bytes_xferred = file_info.upload_offset; sprintf(header, "\n" " %d\n" " %d\n" "\n" "\n" "%s" "\n" "%s" "\n" "\n" "%.0f\n" "%.0f\n" "\n", BOINC_MAJOR_VERSION, BOINC_MINOR_VERSION, file_info.signed_xml, file_info.xml_signature, file_info.nbytes, file_info.upload_offset ); file_size_query = false; return HTTP_OP::init_post2( fip->get_current_url(is_upload), header, pathname, fip->upload_offset ); } } // Parse the file upload handler response in req1 // int FILE_XFER::parse_upload_response(double &nbytes) { int status = ERR_UPLOAD_TRANSIENT, x; char buf[256]; nbytes = -1; parse_double(req1, "", nbytes); if (parse_int(req1, "", x)) { switch (x) { case -1: status = ERR_UPLOAD_PERMANENT; break; case 0: status = 0; break; case 1: status = ERR_UPLOAD_TRANSIENT; break; default: status = ERR_UPLOAD_TRANSIENT; break; } } else { status = ERR_UPLOAD_TRANSIENT; } if (parse_str(req1, "", buf, sizeof(buf))) { msg_printf(fip->project, MSG_ERROR, "Error on file upload: %s", buf); } return status; } // Create a new empty FILE_XFER_SET // FILE_XFER_SET::FILE_XFER_SET(HTTP_OP_SET* p) { http_ops = p; } // start a FILE_XFER going (connect to server etc.) // If successful, add to the set // int FILE_XFER_SET::insert(FILE_XFER* fxp) { int retval; retval = http_ops->insert(fxp); if (retval) return retval; file_xfers.push_back(fxp); return 0; } // Remove a FILE_XFER object from the set // int FILE_XFER_SET::remove(FILE_XFER* fxp) { vector::iterator iter; http_ops->remove(fxp); iter = file_xfers.begin(); while (iter != file_xfers.end()) { if (*iter == fxp) { file_xfers.erase(iter); return 0; } iter++; } msg_printf(NULL, MSG_ERROR, "FILE_XFER_SET::remove(): not found\n"); return ERR_NOT_FOUND; } // Run through the FILE_XFER_SET and determine if any of the file // transfers are complete or had an error // bool FILE_XFER_SET::poll(double now) { unsigned int i; FILE_XFER* fxp; bool action = false; static double last_time=0; if (now - last_time < 1.0) return false; last_time = now; SCOPE_MSG_LOG scope_messages(log_messages, CLIENT_MSG_LOG::DEBUG_FILE_XFER); for (i=0; ihttp_op_done()) { action = true; fxp->file_xfer_done = true; scope_messages.printf("FILE_XFER_SET::poll(): http op done; retval %d\n", fxp->http_op_retval); fxp->file_xfer_retval = fxp->http_op_retval; if (fxp->file_xfer_retval == 0) { if (fxp->is_upload) { fxp->file_xfer_retval = fxp->parse_upload_response( fxp->fip->upload_offset ); } // If this was a file size query, restart the transfer // using the remote file size information // if (fxp->file_size_query) { if (fxp->file_xfer_retval) { printf("ERROR: file upload returned %d\n", fxp->file_xfer_retval); fxp->fip->upload_offset = -1; } else { // if the server's file size is bigger than ours, // something bad has happened (like a result // got sent to multiple users). // Pretend the file was successfully uploaded // if (fxp->fip->upload_offset >= fxp->fip->nbytes) { fxp->file_xfer_done = true; fxp->file_xfer_retval = 0; } else { // Restart the upload, using the newly obtained // upload_offset fxp->close_socket(); fxp->file_xfer_retval = fxp->init_upload(*fxp->fip); if (!fxp->file_xfer_retval) { remove(fxp); i--; fxp->file_xfer_retval = insert(fxp); if (!fxp->file_xfer_retval) { fxp->file_xfer_done = false; fxp->file_xfer_retval = 0; fxp->http_op_retval = 0; } } } } } } else if (fxp->file_xfer_retval == HTTP_STATUS_RANGE_REQUEST_ERROR) { fxp->fip->error_msg = "Existing file too large; can't resume"; } } } return action; }