TEST(FileTransfer, DetectsUploadConnectionError) { CURL *curl; std::string source_file = "/accounts/1000/shared/documents/filetransfer_test.txt"; std::string target_url = "http://127.0.0.1/uploader.php"; std::string source_escaped(curl_easy_escape(curl, curl_easy_escape(curl, source_file.c_str(), 0), 0)); std::string target_escaped(curl_easy_escape(curl, curl_easy_escape(curl, target_url.c_str(), 0), 0)); std::string expected = "upload error 3 " + source_escaped + " " + target_escaped + " 0"; int file_result = createTestFile(source_file.c_str()); EXPECT_EQ(0, file_result); webworks::FileUploadInfo upload_info; upload_info.sourceFile = source_file; upload_info.targetURL = target_url; upload_info.mimeType = "text/plain"; upload_info.fileKey = "file"; upload_info.fileName = "test_file.txt"; upload_info.chunkedMode = 0; webworks::FileTransferCurl file_transfer; std::string result = file_transfer.Upload(&upload_info); EXPECT_EQ(expected, result); remove(source_file.c_str()); }
TEST(FileTransfer, DetectsIncorrectUploadFilePath) { CURL *curl; std::string source_file = "/accounts/1000/shared/camera/abcdefg.hij"; std::string target_url = "http://bojap.com/omg/uploader.php"; std::string source_escaped(curl_easy_escape(curl, curl_easy_escape(curl, source_file.c_str(), 0), 0)); std::string target_escaped(curl_easy_escape(curl, curl_easy_escape(curl, target_url.c_str(), 0), 0)); std::string expected = "upload error 1 " + source_escaped + " " + target_escaped + " 0"; webworks::FileUploadInfo upload_info; upload_info.sourceFile = source_file; upload_info.targetURL = target_url; upload_info.mimeType = "image/jpeg"; upload_info.fileKey = "image"; upload_info.fileName = "new_image.jpg"; upload_info.chunkedMode = 1; webworks::FileTransferCurl file_transfer; std::string result = file_transfer.Upload(&upload_info); EXPECT_EQ(expected, result); }
// Tests for invalid target (permissions error) TEST(FileTransfer, DetectsInvalidDownloadTargetPermissions) { CURL *curl; std::string source = "http://www.google.ca/ig/images/jfk/google_color.png"; std::string target = "/accounts/hello.jpg"; std::string source_escaped(curl_easy_escape(curl, curl_easy_escape(curl, source.c_str(), 0), 0)); std::string target_escaped(curl_easy_escape(curl, curl_easy_escape(curl, target.c_str(), 0), 0)); std::string expected = "download error 4 " + source_escaped + " " + target_escaped + " 0"; webworks::FileDownloadInfo download_info; download_info.source = source; download_info.target = target; webworks::FileTransferCurl file_transfer; std::string result = file_transfer.Download(&download_info); EXPECT_EQ(expected, result); }
// Tests for connetion error TEST(FileTransfer, DetectsIncorrectDownloadSource) { CURL *curl; std::string source = "http://domain.does.not.exist/hello.jpg"; std::string target = "/accounts/1000/shared/camera/hello.jpg"; std::string source_escaped(curl_easy_escape(curl, curl_easy_escape(curl, source.c_str(), 0), 0)); std::string target_escaped(curl_easy_escape(curl, curl_easy_escape(curl, target.c_str(), 0), 0)); std::string expected = "download error 3 " + source_escaped + " " + target_escaped + " 0"; webworks::FileDownloadInfo download_info; download_info.source = source; download_info.target = target; webworks::FileTransferCurl file_transfer; std::string result = file_transfer.Download(&download_info); EXPECT_EQ(expected, result); }
std::string FileTransferCurl::Download(FileDownloadInfo *downloadInfo) { CURL *curl; FILE *fp; CURLcode result; std::string result_string; bool error = 0; int http_status = 0; const char *source = (downloadInfo->source).c_str(); const char *target = (downloadInfo->target).c_str(); std::string source_escaped(curl_easy_escape(curl, curl_easy_escape(curl, source, 0), 0)); std::string target_escaped(curl_easy_escape(curl, curl_easy_escape(curl, target, 0), 0)); const char *targetDir = downloadInfo->target.substr(0, downloadInfo->target.find_last_of('/')).c_str(); // Check if target directory exists with write permissions if (access(targetDir, R_OK)) { if (mkdir_p(targetDir, S_IRWXU | S_IRWXG)) { return buildDownloadErrorString(PERMISSIONS_ERR, source_escaped, target_escaped, http_status); } } curl = curl_easy_init(); if (!curl) { return buildDownloadErrorString(CONNECTION_ERR, source_escaped, target_escaped, http_status); } fp = fopen(target, "wb"); curl_easy_setopt(curl, CURLOPT_URL, source); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, DownloadWriteCallback); curl_easy_setopt(curl, CURLOPT_WRITEDATA, fp); // Check domain bool blockedDomain = false; const std::string parsedDomain(parseDomain(downloadInfo->source.c_str())); const DomainVerifyMap::iterator findDomain = m_pVerifyMap->find(parsedDomain); if (findDomain != m_pVerifyMap->end()) { if (findDomain->second) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); } else { blockedDomain = true; } } result = curl_easy_perform(curl); if (result == CURLE_SSL_CACERT) { if (!blockedDomain) { result = openDialog(curl, downloadInfo->windowGroup, parsedDomain); } } if (result == CURLE_OK) { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status); if (http_status >= 200 && http_status < 300) { result_string = buildDownloadSuccessString(true, false, downloadInfo->source.substr(downloadInfo->source.find_last_of('/')+1), downloadInfo->target); } else if (http_status == 404) { error = 1; result_string = buildDownloadErrorString(FILE_NOT_FOUND_ERR, source_escaped, target_escaped, http_status); } else if (http_status >= 400 && http_status < 500) { error = 1; result_string = buildDownloadErrorString(INVALID_URL_ERR, source_escaped, target_escaped, http_status); } else { error = 1; result_string = buildDownloadErrorString(CONNECTION_ERR, source_escaped, target_escaped, http_status); } } else { FileTransferErrorCodes error_code; switch (result) { case CURLE_READ_ERROR: case CURLE_FILE_COULDNT_READ_FILE: error_code = FILE_NOT_FOUND_ERR; break; case CURLE_URL_MALFORMAT: error_code = INVALID_URL_ERR; break; default: error_code = CONNECTION_ERR; break; } error = 1; result_string = buildDownloadErrorString(error_code, source_escaped, target_escaped, http_status); } fclose(fp); if (error) { remove(downloadInfo->target.c_str()); } curl_easy_cleanup(curl); return result_string; }
std::string FileTransferCurl::Upload(FileUploadInfo *uploadInfo) { CURL *curl; CURLcode result; std::string result_string; struct curl_httppost *formpost = NULL; struct curl_httppost *lastptr = NULL; struct curl_slist *headerlist = NULL; FILE *upload_file = NULL; std::string source_escaped(curl_easy_escape(curl, curl_easy_escape(curl, uploadInfo->sourceFile.c_str(), 0), 0)); std::string target_escaped(curl_easy_escape(curl, curl_easy_escape(curl, uploadInfo->targetURL.c_str(), 0), 0)); int http_status = 0; // Initialize the easy interface for curl curl = curl_easy_init(); if (!curl) { return buildUploadErrorString(CONNECTION_ERR, source_escaped, target_escaped, http_status); } // Set up the form and fill in the file upload fields if (uploadInfo->chunkedMode) { upload_file = fopen(uploadInfo->sourceFile.c_str(), "r"); if (!upload_file) { return buildUploadErrorString(FILE_NOT_FOUND_ERR, source_escaped, target_escaped, http_status); } // Find the file size fseek(upload_file, 0L, SEEK_END); int file_size = ftell(upload_file); rewind(upload_file); uploadAttributes uploadAtt; uploadAtt.file = upload_file; uploadAtt.max_chunk_size = uploadInfo->chunkSize; curl_formadd(&formpost, &lastptr, CURLFORM_STREAM, &uploadAtt, CURLFORM_CONTENTSLENGTH, file_size, CURLFORM_COPYNAME, uploadInfo->fileKey.c_str(), CURLFORM_FILENAME, uploadInfo->fileName.c_str(), CURLFORM_CONTENTTYPE, uploadInfo->mimeType.c_str(), CURLFORM_END); } else { curl_formadd(&formpost, &lastptr, CURLFORM_FILE, uploadInfo->sourceFile.c_str(), CURLFORM_COPYNAME, uploadInfo->fileKey.c_str(), CURLFORM_FILENAME, uploadInfo->fileName.c_str(), CURLFORM_CONTENTTYPE, uploadInfo->mimeType.c_str(), CURLFORM_END); } if (uploadInfo->params.size() > 0) { std::vector<std::string>::const_iterator it; for (it = uploadInfo->params.begin(); it < uploadInfo->params.end(); it++) { const char *key = it->c_str(); it++; const char *value = it->c_str(); curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, key, CURLFORM_COPYCONTENTS, value, CURLFORM_END); } } // Set up the headers headerlist = curl_slist_append(headerlist, "Expect:"); if (uploadInfo->chunkedMode) { headerlist = curl_slist_append(headerlist, "Transfer-Encoding: chunked"); } // Set up the callbacks std::string write_data; curl_easy_setopt(curl, CURLOPT_WRITEDATA, static_cast<void *>(&write_data)); curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, UploadWriteCallback); if (uploadInfo->chunkedMode) { curl_easy_setopt(curl, CURLOPT_READFUNCTION, UploadReadCallback); } // Allow redirects curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1); curl_easy_setopt(curl, CURLOPT_POSTREDIR, CURL_REDIR_POST_ALL); // Attach the different components curl_easy_setopt(curl, CURLOPT_HTTPPOST, formpost); curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headerlist); curl_easy_setopt(curl, CURLOPT_URL, uploadInfo->targetURL.c_str()); curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 1); // Check domain bool blockedDomain = false; const std::string parsedDomain(parseDomain(uploadInfo->targetURL.c_str())); const DomainVerifyMap::iterator findDomain = m_pVerifyMap->find(parsedDomain); if (findDomain != m_pVerifyMap->end()) { if (findDomain->second) { curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L); } else { blockedDomain = true; } } // Perform file transfer (blocking) result = curl_easy_perform(curl); if (result == CURLE_SSL_CACERT) { if (!blockedDomain) { result = openDialog(curl, uploadInfo->windowGroup, parsedDomain); } } if (result == CURLE_OK) { curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_status); if (http_status >= 200 && http_status < 300) { double bytes_sent; curl_easy_getinfo(curl, CURLINFO_CONTENT_LENGTH_UPLOAD, &bytes_sent); result_string = buildUploadSuccessString(bytes_sent, http_status, write_data); } else if (http_status == 404) { result_string = buildUploadErrorString(INVALID_URL_ERR, source_escaped, target_escaped, http_status); } else { result_string = buildUploadErrorString(CONNECTION_ERR, source_escaped, target_escaped, http_status); } } else { FileTransferErrorCodes error_code; switch (result) { case CURLE_READ_ERROR: case CURLE_FILE_COULDNT_READ_FILE: error_code = FILE_NOT_FOUND_ERR; break; case CURLE_URL_MALFORMAT: error_code = INVALID_URL_ERR; break; default: error_code = CONNECTION_ERR; break; } result_string = buildUploadErrorString(error_code, source_escaped, target_escaped, http_status); } // Clean up if (uploadInfo->chunkedMode) { fclose(upload_file); } curl_easy_cleanup(curl); curl_formfree(formpost); curl_slist_free_all(headerlist); return result_string; }