From 35e216740143fe76d5d3485acb06132c6bdfe974 Mon Sep 17 00:00:00 2001 From: Mohamed Amine Mzoughi Date: Thu, 23 Jun 2022 12:56:59 +0200 Subject: [PATCH 01/11] Fixed Info() returning true with a bad file size and added a boolean to set progress callback state. --- FTP/FTPClient.cpp | 8 ++++---- FTP/FTPClient.h | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/FTP/FTPClient.cpp b/FTP/FTPClient.cpp index 849eab6..e10fb8c 100644 --- a/FTP/FTPClient.cpp +++ b/FTP/FTPClient.cpp @@ -146,12 +146,12 @@ bool CFTPClient::CleanupSession() { * @param [in] fnCallback callback to progress function * */ -void CFTPClient::SetProgressFnCallback(void *pOwner, const ProgressFnCallback &fnCallback) { +void CFTPClient::SetProgressFnCallback(void *pOwner, const ProgressFnCallback &fnCallback, const bool enable /*= true*/) { m_ProgressStruct.pOwner = pOwner; m_fnProgressCallback = fnCallback; m_ProgressStruct.pCurl = m_pCurlSession; m_ProgressStruct.dLastRunTime = 0; - m_bProgressCallbackSet = true; + m_bProgressCallbackSet = enable; } /** @@ -532,8 +532,8 @@ bool CFTPClient::Info(const std::string &strRemoteFile, struct FileInfo &oFileIn } res = curl_easy_getinfo(m_pCurlSession, CURLINFO_CONTENT_LENGTH_DOWNLOAD, &oFileInfo.dFileSize); - if (CURLE_OK == res && oFileInfo.dFileSize > 0.0) { - bRes = true; + if (CURLE_OK != res || oFileInfo.dFileSize < 0.0) { + bRes = false; } } else if (m_eSettingsFlags & ENABLE_LOG) m_oLog(StringFormat(LOG_ERROR_CURL_FILETIME_FORMAT, strRemoteFile.c_str(), res, curl_easy_strerror(res))); diff --git a/FTP/FTPClient.h b/FTP/FTPClient.h index 76f3854..6a0426e 100644 --- a/FTP/FTPClient.h +++ b/FTP/FTPClient.h @@ -118,7 +118,7 @@ class CFTPClient { CFTPClient &operator=(CFTPClient &&) = delete; // Setters - Getters (for unit tests) - void SetProgressFnCallback(void *pOwner, const ProgressFnCallback &fnCallback); + void SetProgressFnCallback(void *pOwner, const ProgressFnCallback &fnCallback, const bool enable = true); void SetProxy(const std::string &strProxy); void SetProxyUserPwd(const std::string &strProxyUserPwd); inline void SetTimeout(const int &iTimeout) { m_iCurlTimeout = iTimeout; } From 68a0f164c48d0167ecb7310929d1a906bdba5ff6 Mon Sep 17 00:00:00 2001 From: Andrew Date: Thu, 4 Aug 2022 18:44:40 +0300 Subject: [PATCH 02/11] Implement uploading from std::istream or custom read function. --- FTP/FTPClient.cpp | 133 +++++++++++++++++++++++++++++++--------------- FTP/FTPClient.h | 9 +++- 2 files changed, 98 insertions(+), 44 deletions(-) diff --git a/FTP/FTPClient.cpp b/FTP/FTPClient.cpp index e10fb8c..a75e282 100644 --- a/FTP/FTPClient.cpp +++ b/FTP/FTPClient.cpp @@ -781,6 +781,90 @@ bool CFTPClient::DownloadWildcard(const std::string &strLocalDir, const std::str return bRet; } +/** + * @brief uploads a user data using readFn to a remote folder. + * + * @param [in] readFn Reading function, corresponds to CURLOPT_READFUNCTION. + * @param [in] userData user data passed to readFn as last parameter. + * @param [in] strRemoteFile Complete URN of the remote location (with the file + * name) encoded in UTF-8 format. + * @param [in] bCreateDir Enable or disable creation of remote missing + * directories contained in the URN. + * + * @retval true Data successfully uploaded. + * @retval false Data couldn't be uploaded. Check the log messages for more + * information. + */ +bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const std::string &strRemoteFile, + const bool &bCreateDir, long fileSize) const { + if (readFn == nullptr || strRemoteFile.empty()) + return false; + + if (!m_pCurlSession) { + if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); + + return false; + } + // Reset is mandatory to avoid bad surprises + curl_easy_reset(m_pCurlSession); + + std::string strLocalRemoteFile = ParseURL(strRemoteFile); + + bool bRes = false; + + /* specify target */ + curl_easy_setopt(m_pCurlSession, CURLOPT_URL, strLocalRemoteFile.c_str()); + + /* we want to use our own read function */ + curl_easy_setopt(m_pCurlSession, CURLOPT_READFUNCTION, readFn); + + /* now specify which file to upload */ + curl_easy_setopt(m_pCurlSession, CURLOPT_READDATA, userData); + + /* Set the size of the file to upload (optional). If you give a *_LARGE + option you MUST make sure that the type of the passed-in argument is a + curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must + make sure that to pass in a type 'long' argument. */ + curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, static_cast(fileSize)); + + /* enable uploading */ + curl_easy_setopt(m_pCurlSession, CURLOPT_UPLOAD, 1L); + + if (bCreateDir) curl_easy_setopt(m_pCurlSession, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR); + + CURLcode res = Perform(); + + if (res != CURLE_OK) { + if (m_eSettingsFlags & ENABLE_LOG) + m_oLog(StringFormat(LOG_ERROR_CURL_UPLOAD_FORMAT, res, curl_easy_strerror(res))); + } else + bRes = true; + + return bRes; +} + +/** + * @brief uploads data from a stream to a remote folder. + * + * @param [in] inputStream Stream containing data which will be uploaded. + * @param [in] strRemoteFile Complete URN of the remote location (with the file + * name) encoded in UTF-8 format. + * @param [in] bCreateDir Enable or disable creation of remote missing + * directories contained in the URN. + * @param [in] fileSize + * + * @retval true Successfully uploaded the inputStream. + * @retval false The inputStream couldn't be uploaded. Check the log messages for more + * information. + */ +bool CFTPClient::UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir, + long fileSize) const { + if ( !inputStream ) + return false; + + return UploadFile(ReadFromStreamCallback, static_cast(&inputStream), strRemoteFile, bCreateDir, fileSize); +} + /** * @brief uploads a local file to a remote folder. * @@ -807,16 +891,7 @@ bool CFTPClient::DownloadWildcard(const std::string &strLocalDir, const std::str bool CFTPClient::UploadFile(const std::string &strLocalFile, const std::string &strRemoteFile, const bool &bCreateDir) const { if (strLocalFile.empty() || strRemoteFile.empty()) return false; - if (!m_pCurlSession) { - if (m_eSettingsFlags & ENABLE_LOG) m_oLog(LOG_ERROR_CURL_NOT_INIT_MSG); - - return false; - } - // Reset is mandatory to avoid bad surprises - curl_easy_reset(m_pCurlSession); - std::ifstream InputFile; - std::string strLocalRemoteFile = ParseURL(strRemoteFile); struct stat file_info; bool bRes = false; @@ -837,35 +912,7 @@ bool CFTPClient::UploadFile(const std::string &strLocalFile, const std::string & return false; } - /* specify target */ - curl_easy_setopt(m_pCurlSession, CURLOPT_URL, strLocalRemoteFile.c_str()); - - /* we want to use our own read function */ - curl_easy_setopt(m_pCurlSession, CURLOPT_READFUNCTION, ReadFromFileCallback); - - /* now specify which file to upload */ - curl_easy_setopt(m_pCurlSession, CURLOPT_READDATA, &InputFile); - - /* Set the size of the file to upload (optional). If you give a *_LARGE - option you MUST make sure that the type of the passed-in argument is a - curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must - make sure that to pass in a type 'long' argument. */ - curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, static_cast(file_info.st_size)); - - /* enable uploading */ - curl_easy_setopt(m_pCurlSession, CURLOPT_UPLOAD, 1L); - - if (bCreateDir) curl_easy_setopt(m_pCurlSession, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR); - - // TODO add the possibility to rename the file upon upload finish.... - - CURLcode res = Perform(); - - if (res != CURLE_OK) { - if (m_eSettingsFlags & ENABLE_LOG) - m_oLog(StringFormat(LOG_ERROR_CURL_UPLOAD_FORMAT, strLocalFile.c_str(), res, curl_easy_strerror(res))); - } else - bRes = true; + bRes = UploadFile(InputFile, strRemoteFile, bCreateDir, file_info.st_size); } InputFile.close(); @@ -919,7 +966,7 @@ bool CFTPClient::AppendFile(const std::string &strLocalFile, const size_t fileOf curl_easy_setopt(m_pCurlSession, CURLOPT_URL, strLocalRemoteFile.c_str()); /* we want to use our own read function */ - curl_easy_setopt(m_pCurlSession, CURLOPT_READFUNCTION, ReadFromFileCallback); + curl_easy_setopt(m_pCurlSession, CURLOPT_READFUNCTION, ReadFromStreamCallback); /* now specify which file to upload */ curl_easy_setopt(m_pCurlSession, CURLOPT_READDATA, &InputFile); @@ -1161,13 +1208,13 @@ size_t CFTPClient::WriteToMemory(void *buff, size_t size, size_t nmemb, void *da * @param ptr pointer of max size (size*nmemb) to write data to it * @param size size parameter * @param nmemb memblock parameter - * @param stream pointer to user data (file stream) + * @param stream pointer to user data (input stream) * * @return (size * nmemb) */ -size_t CFTPClient::ReadFromFileCallback(void *ptr, size_t size, size_t nmemb, void *stream) { - std::ifstream *pFileStream = reinterpret_cast(stream); - if (pFileStream->is_open()) { +size_t CFTPClient::ReadFromStreamCallback(void *ptr, size_t size, size_t nmemb, void *stream) { + auto *pFileStream = reinterpret_cast(stream); + if (!pFileStream->fail()) { pFileStream->read(reinterpret_cast(ptr), size * nmemb); return pFileStream->gcount(); } diff --git a/FTP/FTPClient.h b/FTP/FTPClient.h index 6a0426e..a1c08e6 100644 --- a/FTP/FTPClient.h +++ b/FTP/FTPClient.h @@ -42,6 +42,7 @@ class CFTPClient { // Public definitions using ProgressFnCallback = std::function; using LogFnCallback = std::function; + using CurlReadFn = size_t (*) (void *, size_t, size_t, void *); // Used to download many items at once struct WildcardTransfersCallbackData { @@ -164,6 +165,12 @@ class CFTPClient { bool DownloadWildcard(const std::string &strLocalDir, const std::string &strRemoteWildcard) const; + bool UploadFile(CurlReadFn readFn, void *userData, const std::string &strRemoteFile, const bool &bCreateDir = false, + long fileSize = -1) const; + + bool UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir = false, + long fileSize = -1) const; + bool UploadFile(const std::string &strLocalFile, const std::string &strRemoteFile, const bool &bCreateDir = false) const; bool AppendFile(const std::string &strLocalFile, const size_t fileOffset, const std::string &strRemoteFile, @@ -196,7 +203,7 @@ class CFTPClient { // Curl callbacks static size_t WriteInStringCallback(void *ptr, size_t size, size_t nmemb, void *data); static size_t WriteToFileCallback(void *ptr, size_t size, size_t nmemb, void *data); - static size_t ReadFromFileCallback(void *ptr, size_t size, size_t nmemb, void *stream); + static size_t ReadFromStreamCallback(void *ptr, size_t size, size_t nmemb, void *stream); static size_t ThrowAwayCallback(void *ptr, size_t size, size_t nmemb, void *data); static size_t WriteToMemory(void *ptr, size_t size, size_t nmemb, void *data); From 7cfbc362244bea199ec474b19884653621085178 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 6 Aug 2022 11:03:02 +0300 Subject: [PATCH 03/11] Set filesSize type to curl_off_t and added test for streams. --- FTP/FTPClient.cpp | 6 ++--- FTP/FTPClient.h | 4 +-- TestFTP/main.cpp | 67 ++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 65 insertions(+), 12 deletions(-) diff --git a/FTP/FTPClient.cpp b/FTP/FTPClient.cpp index a75e282..2f1c126 100644 --- a/FTP/FTPClient.cpp +++ b/FTP/FTPClient.cpp @@ -796,7 +796,7 @@ bool CFTPClient::DownloadWildcard(const std::string &strLocalDir, const std::str * information. */ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const std::string &strRemoteFile, - const bool &bCreateDir, long fileSize) const { + const bool &bCreateDir, curl_off_t fileSize) const { if (readFn == nullptr || strRemoteFile.empty()) return false; @@ -825,7 +825,7 @@ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const option you MUST make sure that the type of the passed-in argument is a curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must make sure that to pass in a type 'long' argument. */ - curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, static_cast(fileSize)); + curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, fileSize); /* enable uploading */ curl_easy_setopt(m_pCurlSession, CURLOPT_UPLOAD, 1L); @@ -858,7 +858,7 @@ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const * information. */ bool CFTPClient::UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir, - long fileSize) const { + curl_off_t fileSize) const { if ( !inputStream ) return false; diff --git a/FTP/FTPClient.h b/FTP/FTPClient.h index a1c08e6..8625630 100644 --- a/FTP/FTPClient.h +++ b/FTP/FTPClient.h @@ -166,10 +166,10 @@ class CFTPClient { bool DownloadWildcard(const std::string &strLocalDir, const std::string &strRemoteWildcard) const; bool UploadFile(CurlReadFn readFn, void *userData, const std::string &strRemoteFile, const bool &bCreateDir = false, - long fileSize = -1) const; + curl_off_t fileSize = -1) const; bool UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir = false, - long fileSize = -1) const; + curl_off_t fileSize = -1) const; bool UploadFile(const std::string &strLocalFile, const std::string &strRemoteFile, const bool &bCreateDir = false) const; diff --git a/TestFTP/main.cpp b/TestFTP/main.cpp index bd7c0c2..548d0c6 100644 --- a/TestFTP/main.cpp +++ b/TestFTP/main.cpp @@ -243,7 +243,7 @@ TEST_F(FTPClientTest, TestSaveFileNameWithAccents) { // Convert file name from ANSI to UTF8 std::string remoteFileUtf8 = CFTPClient::AnsiToUtf8(FTP_REMOTE_FILE); - std::string localFileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_téléchargé"); + std::string localFileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_t�l�charg�"); ASSERT_TRUE(m_pFTPClient->DownloadFile(localFileNameUtf8, remoteFileUtf8)); @@ -252,13 +252,13 @@ TEST_F(FTPClientTest, TestSaveFileNameWithAccents) { /* check the SHA1 sum of the downloaded file if possible */ if (!FTP_REMOTE_FILE_SHA1SUM.empty()) { - std::string ret = sha1sum("fichier_téléchargé"); + std::string ret = sha1sum("fichier_t�l�charg�"); std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); EXPECT_TRUE(FTP_REMOTE_FILE_SHA1SUM == ret); } /* delete test file */ - EXPECT_TRUE(remove("fichier_téléchargé") == 0); + EXPECT_TRUE(remove("fichier_t�l�charg�") == 0); } else std::cout << "FTP tests are disabled !" << std::endl; } @@ -471,10 +471,10 @@ TEST_F(FTPClientTest, TestUploadFileNameWithAccents) { TimeStampTest(ssTimestamp); // Convert file name from ANSI to UTF8 - std::string fileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_à_téléverser.txt"); + std::string fileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_�_t�l�verser.txt"); // create dummy test file - std::ofstream ofTestUpload("fichier_à_téléverser.txt"); + std::ofstream ofTestUpload("fichier_�_t�l�verser.txt"); ASSERT_TRUE(static_cast(ofTestUpload)); ofTestUpload << "Unit Test TestUploadFile executed on " + ssTimestamp.str() + "\n" + @@ -502,7 +502,7 @@ TEST_F(FTPClientTest, TestUploadFileNameWithAccents) { std::cout << std::endl; /* check the SHA1 sum of the uploaded file */ - std::string expectedSha1Sum = sha1sum("fichier_à_téléverser.txt"); + std::string expectedSha1Sum = sha1sum("fichier_�_t�l�verser.txt"); std::string resultSha1Sum = sha1sum(uploadedFileBytes); EXPECT_TRUE(expectedSha1Sum == resultSha1Sum); @@ -512,7 +512,7 @@ TEST_F(FTPClientTest, TestUploadFileNameWithAccents) { ASSERT_TRUE(m_pFTPClient->RemoveFile(FTP_REMOTE_UPLOAD_FOLDER + fileNameUtf8)); // delete test file - EXPECT_TRUE(remove("fichier_à_téléverser.txt") == 0); + EXPECT_TRUE(remove("fichier_�_t�l�verser.txt") == 0); } else std::cout << "FTP tests are disabled !" << std::endl; } @@ -815,6 +815,59 @@ TEST_F(SFTPClientTest, TestUploadAndRemoveFile) { std::cout << "SFTP tests are disabled !" << std::endl; } +TEST_F(SFTPClientTest, TestUploadStreamAndRemove) { + if (SFTP_TEST_ENABLED) { + // to display a beautiful progress bar on console + m_pSFTPClient->SetProgressFnCallback(m_pSFTPClient.get(), &TestUPProgressCallback); + + std::ostringstream ssTimestamp; + TimeStampTest(ssTimestamp); + + // create dummy test file + std::stringstream ofTestUpload; + ASSERT_TRUE(static_cast(ofTestUpload)); + + ofTestUpload << "Unit Test TestUploadFile executed on " + ssTimestamp.str() + "\n" + + "This file is uploaded via FTPClient-C++ API.\n" + + "If this file exists, that means that the unit test is passed.\n"; + ASSERT_TRUE(static_cast(ofTestUpload)); + + // Upload file and create a directory "upload_test" + ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "upload_test/test_upload_stream.txt", true)); + + /* to properly show the progress bar */ + std::cout << std::endl; + + // Upload file + ofTestUpload.clear(); + ofTestUpload.seekg(0); + ASSERT_TRUE(static_cast(ofTestUpload)); + ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + + std::cout << std::endl; + + // Download the uploaded file into a vector of bytes + { + std::vector uploadedFileBytes; + EXPECT_TRUE(m_pSFTPClient->DownloadFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt", uploadedFileBytes)); + + std::cout << std::endl; + + /* check the SHA1 sum of the uploaded file */ + std::string contentStr = ofTestUpload.str(); + std::vector contentBytes(contentStr.begin(), contentStr.end() ); + std::string expectedSha1Sum = sha1sum(contentBytes); + std::string resultSha1Sum = sha1sum(uploadedFileBytes); + + EXPECT_TRUE(expectedSha1Sum == resultSha1Sum); + } + + // Remove file + ASSERT_TRUE(m_pSFTPClient->RemoveFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + } else + std::cout << "SFTP tests are disabled !" << std::endl; +} + // TODO : this unit test can't be executed elsewhere #if 0 TEST_F(SFTPClientTest, TestAppend /*AndRemoveFile*/) { From 3f7072ad6c04667667de87fefa89458fbb3eb91b Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 6 Aug 2022 11:24:51 +0300 Subject: [PATCH 04/11] Revert "Set filesSize type to curl_off_t and added test for streams." This reverts commit 7cfbc362244bea199ec474b19884653621085178. --- FTP/FTPClient.cpp | 6 ++--- FTP/FTPClient.h | 4 +-- TestFTP/main.cpp | 67 +++++------------------------------------------ 3 files changed, 12 insertions(+), 65 deletions(-) diff --git a/FTP/FTPClient.cpp b/FTP/FTPClient.cpp index 2f1c126..a75e282 100644 --- a/FTP/FTPClient.cpp +++ b/FTP/FTPClient.cpp @@ -796,7 +796,7 @@ bool CFTPClient::DownloadWildcard(const std::string &strLocalDir, const std::str * information. */ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const std::string &strRemoteFile, - const bool &bCreateDir, curl_off_t fileSize) const { + const bool &bCreateDir, long fileSize) const { if (readFn == nullptr || strRemoteFile.empty()) return false; @@ -825,7 +825,7 @@ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const option you MUST make sure that the type of the passed-in argument is a curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must make sure that to pass in a type 'long' argument. */ - curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, fileSize); + curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, static_cast(fileSize)); /* enable uploading */ curl_easy_setopt(m_pCurlSession, CURLOPT_UPLOAD, 1L); @@ -858,7 +858,7 @@ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const * information. */ bool CFTPClient::UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir, - curl_off_t fileSize) const { + long fileSize) const { if ( !inputStream ) return false; diff --git a/FTP/FTPClient.h b/FTP/FTPClient.h index 8625630..a1c08e6 100644 --- a/FTP/FTPClient.h +++ b/FTP/FTPClient.h @@ -166,10 +166,10 @@ class CFTPClient { bool DownloadWildcard(const std::string &strLocalDir, const std::string &strRemoteWildcard) const; bool UploadFile(CurlReadFn readFn, void *userData, const std::string &strRemoteFile, const bool &bCreateDir = false, - curl_off_t fileSize = -1) const; + long fileSize = -1) const; bool UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir = false, - curl_off_t fileSize = -1) const; + long fileSize = -1) const; bool UploadFile(const std::string &strLocalFile, const std::string &strRemoteFile, const bool &bCreateDir = false) const; diff --git a/TestFTP/main.cpp b/TestFTP/main.cpp index 548d0c6..bd7c0c2 100644 --- a/TestFTP/main.cpp +++ b/TestFTP/main.cpp @@ -243,7 +243,7 @@ TEST_F(FTPClientTest, TestSaveFileNameWithAccents) { // Convert file name from ANSI to UTF8 std::string remoteFileUtf8 = CFTPClient::AnsiToUtf8(FTP_REMOTE_FILE); - std::string localFileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_t�l�charg�"); + std::string localFileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_téléchargé"); ASSERT_TRUE(m_pFTPClient->DownloadFile(localFileNameUtf8, remoteFileUtf8)); @@ -252,13 +252,13 @@ TEST_F(FTPClientTest, TestSaveFileNameWithAccents) { /* check the SHA1 sum of the downloaded file if possible */ if (!FTP_REMOTE_FILE_SHA1SUM.empty()) { - std::string ret = sha1sum("fichier_t�l�charg�"); + std::string ret = sha1sum("fichier_téléchargé"); std::transform(ret.begin(), ret.end(), ret.begin(), ::tolower); EXPECT_TRUE(FTP_REMOTE_FILE_SHA1SUM == ret); } /* delete test file */ - EXPECT_TRUE(remove("fichier_t�l�charg�") == 0); + EXPECT_TRUE(remove("fichier_téléchargé") == 0); } else std::cout << "FTP tests are disabled !" << std::endl; } @@ -471,10 +471,10 @@ TEST_F(FTPClientTest, TestUploadFileNameWithAccents) { TimeStampTest(ssTimestamp); // Convert file name from ANSI to UTF8 - std::string fileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_�_t�l�verser.txt"); + std::string fileNameUtf8 = CFTPClient::AnsiToUtf8("fichier_à_téléverser.txt"); // create dummy test file - std::ofstream ofTestUpload("fichier_�_t�l�verser.txt"); + std::ofstream ofTestUpload("fichier_à_téléverser.txt"); ASSERT_TRUE(static_cast(ofTestUpload)); ofTestUpload << "Unit Test TestUploadFile executed on " + ssTimestamp.str() + "\n" + @@ -502,7 +502,7 @@ TEST_F(FTPClientTest, TestUploadFileNameWithAccents) { std::cout << std::endl; /* check the SHA1 sum of the uploaded file */ - std::string expectedSha1Sum = sha1sum("fichier_�_t�l�verser.txt"); + std::string expectedSha1Sum = sha1sum("fichier_à_téléverser.txt"); std::string resultSha1Sum = sha1sum(uploadedFileBytes); EXPECT_TRUE(expectedSha1Sum == resultSha1Sum); @@ -512,7 +512,7 @@ TEST_F(FTPClientTest, TestUploadFileNameWithAccents) { ASSERT_TRUE(m_pFTPClient->RemoveFile(FTP_REMOTE_UPLOAD_FOLDER + fileNameUtf8)); // delete test file - EXPECT_TRUE(remove("fichier_�_t�l�verser.txt") == 0); + EXPECT_TRUE(remove("fichier_à_téléverser.txt") == 0); } else std::cout << "FTP tests are disabled !" << std::endl; } @@ -815,59 +815,6 @@ TEST_F(SFTPClientTest, TestUploadAndRemoveFile) { std::cout << "SFTP tests are disabled !" << std::endl; } -TEST_F(SFTPClientTest, TestUploadStreamAndRemove) { - if (SFTP_TEST_ENABLED) { - // to display a beautiful progress bar on console - m_pSFTPClient->SetProgressFnCallback(m_pSFTPClient.get(), &TestUPProgressCallback); - - std::ostringstream ssTimestamp; - TimeStampTest(ssTimestamp); - - // create dummy test file - std::stringstream ofTestUpload; - ASSERT_TRUE(static_cast(ofTestUpload)); - - ofTestUpload << "Unit Test TestUploadFile executed on " + ssTimestamp.str() + "\n" + - "This file is uploaded via FTPClient-C++ API.\n" + - "If this file exists, that means that the unit test is passed.\n"; - ASSERT_TRUE(static_cast(ofTestUpload)); - - // Upload file and create a directory "upload_test" - ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "upload_test/test_upload_stream.txt", true)); - - /* to properly show the progress bar */ - std::cout << std::endl; - - // Upload file - ofTestUpload.clear(); - ofTestUpload.seekg(0); - ASSERT_TRUE(static_cast(ofTestUpload)); - ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); - - std::cout << std::endl; - - // Download the uploaded file into a vector of bytes - { - std::vector uploadedFileBytes; - EXPECT_TRUE(m_pSFTPClient->DownloadFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt", uploadedFileBytes)); - - std::cout << std::endl; - - /* check the SHA1 sum of the uploaded file */ - std::string contentStr = ofTestUpload.str(); - std::vector contentBytes(contentStr.begin(), contentStr.end() ); - std::string expectedSha1Sum = sha1sum(contentBytes); - std::string resultSha1Sum = sha1sum(uploadedFileBytes); - - EXPECT_TRUE(expectedSha1Sum == resultSha1Sum); - } - - // Remove file - ASSERT_TRUE(m_pSFTPClient->RemoveFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); - } else - std::cout << "SFTP tests are disabled !" << std::endl; -} - // TODO : this unit test can't be executed elsewhere #if 0 TEST_F(SFTPClientTest, TestAppend /*AndRemoveFile*/) { From ac7d0b4777a5f4a5f1bff164472255a8754d6568 Mon Sep 17 00:00:00 2001 From: Andrew Date: Sat, 6 Aug 2022 11:27:00 +0300 Subject: [PATCH 05/11] Fixed codeset and curl_off_t. --- FTP/FTPClient.cpp | 6 +++--- FTP/FTPClient.h | 4 ++-- TestFTP/main.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+), 5 deletions(-) diff --git a/FTP/FTPClient.cpp b/FTP/FTPClient.cpp index a75e282..2f1c126 100644 --- a/FTP/FTPClient.cpp +++ b/FTP/FTPClient.cpp @@ -796,7 +796,7 @@ bool CFTPClient::DownloadWildcard(const std::string &strLocalDir, const std::str * information. */ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const std::string &strRemoteFile, - const bool &bCreateDir, long fileSize) const { + const bool &bCreateDir, curl_off_t fileSize) const { if (readFn == nullptr || strRemoteFile.empty()) return false; @@ -825,7 +825,7 @@ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const option you MUST make sure that the type of the passed-in argument is a curl_off_t. If you use CURLOPT_INFILESIZE (without _LARGE) you must make sure that to pass in a type 'long' argument. */ - curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, static_cast(fileSize)); + curl_easy_setopt(m_pCurlSession, CURLOPT_INFILESIZE_LARGE, fileSize); /* enable uploading */ curl_easy_setopt(m_pCurlSession, CURLOPT_UPLOAD, 1L); @@ -858,7 +858,7 @@ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const * information. */ bool CFTPClient::UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir, - long fileSize) const { + curl_off_t fileSize) const { if ( !inputStream ) return false; diff --git a/FTP/FTPClient.h b/FTP/FTPClient.h index a1c08e6..8625630 100644 --- a/FTP/FTPClient.h +++ b/FTP/FTPClient.h @@ -166,10 +166,10 @@ class CFTPClient { bool DownloadWildcard(const std::string &strLocalDir, const std::string &strRemoteWildcard) const; bool UploadFile(CurlReadFn readFn, void *userData, const std::string &strRemoteFile, const bool &bCreateDir = false, - long fileSize = -1) const; + curl_off_t fileSize = -1) const; bool UploadFile(std::istream &inputStream, const std::string &strRemoteFile, const bool &bCreateDir = false, - long fileSize = -1) const; + curl_off_t fileSize = -1) const; bool UploadFile(const std::string &strLocalFile, const std::string &strRemoteFile, const bool &bCreateDir = false) const; diff --git a/TestFTP/main.cpp b/TestFTP/main.cpp index bd7c0c2..b524636 100644 --- a/TestFTP/main.cpp +++ b/TestFTP/main.cpp @@ -461,6 +461,59 @@ TEST_F(FTPClientTest, TestUploadAndRemoveFile) { std::cout << "FTP tests are disabled !" << std::endl; } +TEST_F(SFTPClientTest, TestUploadStreamAndRemove) { + if (SFTP_TEST_ENABLED) { + // to display a beautiful progress bar on console + m_pSFTPClient->SetProgressFnCallback(m_pSFTPClient.get(), &TestUPProgressCallback); + + std::ostringstream ssTimestamp; + TimeStampTest(ssTimestamp); + + // create dummy test file + std::stringstream ofTestUpload; + ASSERT_TRUE(static_cast(ofTestUpload)); + + ofTestUpload << "Unit Test TestUploadFile executed on " + ssTimestamp.str() + "\n" + + "This file is uploaded via FTPClient-C++ API.\n" + + "If this file exists, that means that the unit test is passed.\n"; + ASSERT_TRUE(static_cast(ofTestUpload)); + + // Upload file and create a directory "upload_test" + ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "upload_test/test_upload_stream.txt", true)); + + /* to properly show the progress bar */ + std::cout << std::endl; + + // Upload file + ofTestUpload.clear(); + ofTestUpload.seekg(0); + ASSERT_TRUE(static_cast(ofTestUpload)); + ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + + std::cout << std::endl; + + // Download the uploaded file into a vector of bytes + { + std::vector uploadedFileBytes; + EXPECT_TRUE(m_pSFTPClient->DownloadFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt", uploadedFileBytes)); + + std::cout << std::endl; + + /* check the SHA1 sum of the uploaded file */ + std::string contentStr = ofTestUpload.str(); + std::vector contentBytes(contentStr.begin(), contentStr.end() ); + std::string expectedSha1Sum = sha1sum(contentBytes); + std::string resultSha1Sum = sha1sum(uploadedFileBytes); + + EXPECT_TRUE(expectedSha1Sum == resultSha1Sum); + } + + // Remove file + ASSERT_TRUE(m_pSFTPClient->RemoveFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + } else + std::cout << "SFTP tests are disabled !" << std::endl; +} + #ifdef WINDOWS TEST_F(FTPClientTest, TestUploadFileNameWithAccents) { if (FTP_TEST_ENABLED) { From de4b4f54f533c0fdd813acf6e85ae78832391ee7 Mon Sep 17 00:00:00 2001 From: Mohamed Amine Mzoughi Date: Mon, 8 Aug 2022 12:12:40 +0200 Subject: [PATCH 06/11] Added unsecure FTP stream upload test. --- TestFTP/main.cpp | 81 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 59 insertions(+), 22 deletions(-) diff --git a/TestFTP/main.cpp b/TestFTP/main.cpp index b524636..843285c 100644 --- a/TestFTP/main.cpp +++ b/TestFTP/main.cpp @@ -461,47 +461,39 @@ TEST_F(FTPClientTest, TestUploadAndRemoveFile) { std::cout << "FTP tests are disabled !" << std::endl; } -TEST_F(SFTPClientTest, TestUploadStreamAndRemove) { - if (SFTP_TEST_ENABLED) { +TEST_F(FTPClientTest, TestUploadStreamAndRemove) { + if (FTP_TEST_ENABLED) { // to display a beautiful progress bar on console - m_pSFTPClient->SetProgressFnCallback(m_pSFTPClient.get(), &TestUPProgressCallback); + m_pFTPClient->SetProgressFnCallback(m_pFTPClient.get(), &TestUPProgressCallback); std::ostringstream ssTimestamp; TimeStampTest(ssTimestamp); - // create dummy test file + // create dummy string stream std::stringstream ofTestUpload; ASSERT_TRUE(static_cast(ofTestUpload)); - ofTestUpload << "Unit Test TestUploadFile executed on " + ssTimestamp.str() + "\n" + - "This file is uploaded via FTPClient-C++ API.\n" + - "If this file exists, that means that the unit test is passed.\n"; + ofTestUpload << "Unit Test 'TestUploadStreamAndRemove' executed on " + ssTimestamp.str() + "\n" + + "This file is uploaded via FTPClient-C++ API.\n" + + "If this file exists, that means that the unit test is passed.\n"; ASSERT_TRUE(static_cast(ofTestUpload)); - // Upload file and create a directory "upload_test" - ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "upload_test/test_upload_stream.txt", true)); - - /* to properly show the progress bar */ - std::cout << std::endl; - - // Upload file - ofTestUpload.clear(); - ofTestUpload.seekg(0); + // Upload steam ASSERT_TRUE(static_cast(ofTestUpload)); - ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + ASSERT_TRUE(m_pFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); std::cout << std::endl; - // Download the uploaded file into a vector of bytes + // Download the uploaded stream into a vector of bytes { std::vector uploadedFileBytes; - EXPECT_TRUE(m_pSFTPClient->DownloadFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt", uploadedFileBytes)); + EXPECT_TRUE(m_pFTPClient->DownloadFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt", uploadedFileBytes)); std::cout << std::endl; /* check the SHA1 sum of the uploaded file */ std::string contentStr = ofTestUpload.str(); - std::vector contentBytes(contentStr.begin(), contentStr.end() ); + std::vector contentBytes(contentStr.begin(), contentStr.end()); std::string expectedSha1Sum = sha1sum(contentBytes); std::string resultSha1Sum = sha1sum(uploadedFileBytes); @@ -509,9 +501,9 @@ TEST_F(SFTPClientTest, TestUploadStreamAndRemove) { } // Remove file - ASSERT_TRUE(m_pSFTPClient->RemoveFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + ASSERT_TRUE(m_pFTPClient->RemoveFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); } else - std::cout << "SFTP tests are disabled !" << std::endl; + std::cout << "FTP tests are disabled !" << std::endl; } #ifdef WINDOWS @@ -868,6 +860,51 @@ TEST_F(SFTPClientTest, TestUploadAndRemoveFile) { std::cout << "SFTP tests are disabled !" << std::endl; } +TEST_F(SFTPClientTest, TestUploadStreamAndRemove) { + if (SFTP_TEST_ENABLED) { + // to display a beautiful progress bar on console + m_pSFTPClient->SetProgressFnCallback(m_pSFTPClient.get(), &TestUPProgressCallback); + + std::ostringstream ssTimestamp; + TimeStampTest(ssTimestamp); + + // create dummy string stream + std::stringstream ofTestUpload; + ASSERT_TRUE(static_cast(ofTestUpload)); + + ofTestUpload << "Unit Test 'TestUploadStreamAndRemove' executed on " + ssTimestamp.str() + "\n" + + "This file is uploaded via FTPClient-C++ API.\n" + + "If this file exists, that means that the unit test is passed.\n"; + ASSERT_TRUE(static_cast(ofTestUpload)); + + // Upload steam + ASSERT_TRUE(static_cast(ofTestUpload)); + ASSERT_TRUE(m_pSFTPClient->UploadFile(ofTestUpload, SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + + std::cout << std::endl; + + // Download the uploaded stream into a vector of bytes + { + std::vector uploadedFileBytes; + EXPECT_TRUE(m_pSFTPClient->DownloadFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt", uploadedFileBytes)); + + std::cout << std::endl; + + /* check the SHA1 sum of the uploaded file */ + std::string contentStr = ofTestUpload.str(); + std::vector contentBytes(contentStr.begin(), contentStr.end()); + std::string expectedSha1Sum = sha1sum(contentBytes); + std::string resultSha1Sum = sha1sum(uploadedFileBytes); + + EXPECT_TRUE(expectedSha1Sum == resultSha1Sum); + } + + // Remove file + ASSERT_TRUE(m_pSFTPClient->RemoveFile(SFTP_REMOTE_UPLOAD_FOLDER + "test_upload_stream.txt")); + } else + std::cout << "SFTP tests are disabled !" << std::endl; +} + // TODO : this unit test can't be executed elsewhere #if 0 TEST_F(SFTPClientTest, TestAppend /*AndRemoveFile*/) { From 80f05b05c49f694ed7a2f5b37ab692675f7a7512 Mon Sep 17 00:00:00 2001 From: keineahnung2345 Date: Tue, 6 Dec 2022 15:20:17 +0800 Subject: [PATCH 07/11] GlobalTestInit: return false if failing to load config file --- TestFTP/test_utils.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TestFTP/test_utils.cpp b/TestFTP/test_utils.cpp index d491bc0..e198e2e 100644 --- a/TestFTP/test_utils.cpp +++ b/TestFTP/test_utils.cpp @@ -36,7 +36,7 @@ std::mutex g_mtxConsoleMutex; bool GlobalTestInit(const std::string& strConfFile) { CSimpleIniA ini; - ini.LoadFile(strConfFile.c_str()); + if (ini.LoadFile(strConfFile.c_str()) != SI_Error::SI_OK) return false; std::string strTmp; strTmp = ini.GetValue("tests", "ftp", ""); From 9b33430d1bcd62ee9a8006d50c1f2ba0502f3504 Mon Sep 17 00:00:00 2001 From: keineahnung2345 Date: Tue, 6 Dec 2022 15:27:39 +0800 Subject: [PATCH 08/11] Make it compilable with C++17 Ref: https://github.com/brofield/simpleini/pull/37 --- TestFTP/simpleini/SimpleIni.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/TestFTP/simpleini/SimpleIni.h b/TestFTP/simpleini/SimpleIni.h index 0da9414..8f0d322 100644 --- a/TestFTP/simpleini/SimpleIni.h +++ b/TestFTP/simpleini/SimpleIni.h @@ -319,7 +319,7 @@ class CSimpleIniTempl { #endif /** Strict less ordering by name of key only */ - struct KeyOrder : std::binary_function { + struct KeyOrder { bool operator()(const Entry &lhs, const Entry &rhs) const { const static SI_STRLESS isLess = SI_STRLESS(); return isLess(lhs.pItem, rhs.pItem); @@ -327,7 +327,7 @@ class CSimpleIniTempl { }; /** Strict less ordering by order, and then name of key */ - struct LoadOrder : std::binary_function { + struct LoadOrder { bool operator()(const Entry &lhs, const Entry &rhs) const { if (lhs.nOrder != rhs.nOrder) { return lhs.nOrder < rhs.nOrder; From b771326ebc4a6a925e70cc2278aa8977324224da Mon Sep 17 00:00:00 2001 From: keineahnung2345 Date: Tue, 6 Dec 2022 16:46:42 +0800 Subject: [PATCH 09/11] Fix typo in README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d39c05e..615ffd0 100644 --- a/README.md +++ b/README.md @@ -311,7 +311,7 @@ Example : (Run only FTP tests) ftp=yes ; SFTP tests (not implemented) are disabled sftp=no -; HTTP Proxy tests are disabled +; HTTP Proxy tests are enabled http-proxy=yes [ftp] From 9bec894599d57adde0b2281a7eb9b1fb932f5364 Mon Sep 17 00:00:00 2001 From: keineahnung2345 Date: Tue, 6 Dec 2022 17:28:29 +0800 Subject: [PATCH 10/11] [fix] LOG_ERROR_CURL_UPLOAD_FORMAT takes three arguments --- FTP/FTPClient.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/FTP/FTPClient.cpp b/FTP/FTPClient.cpp index 2f1c126..d28e9eb 100644 --- a/FTP/FTPClient.cpp +++ b/FTP/FTPClient.cpp @@ -836,7 +836,7 @@ bool CFTPClient::UploadFile(CFTPClient::CurlReadFn readFn, void *userData, const if (res != CURLE_OK) { if (m_eSettingsFlags & ENABLE_LOG) - m_oLog(StringFormat(LOG_ERROR_CURL_UPLOAD_FORMAT, res, curl_easy_strerror(res))); + m_oLog(StringFormat(LOG_ERROR_CURL_UPLOAD_FORMAT, strRemoteFile.c_str(), res, curl_easy_strerror(res))); } else bRes = true; From a5edd9d0f9e372e748e7c8feece65040d49864bc Mon Sep 17 00:00:00 2001 From: EugeneSakun Date: Tue, 7 Feb 2023 17:09:05 +0200 Subject: [PATCH 11/11] Fix protocol parsing --- FTP/FTPClient.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/FTP/FTPClient.cpp b/FTP/FTPClient.cpp index d28e9eb..b6264f2 100644 --- a/FTP/FTPClient.cpp +++ b/FTP/FTPClient.cpp @@ -166,7 +166,7 @@ void CFTPClient::SetProxy(const std::string &strProxy) { std::string strUri = strProxy; std::transform(strUri.begin(), strUri.end(), strUri.begin(), ::toupper); - if (strUri.compare(0, 4, "HTTP") != 0) + if (strUri.compare(0, 5, "HTTP:") != 0) m_strProxy = "http://" + strProxy; else m_strProxy = strProxy; @@ -208,7 +208,7 @@ std::string CFTPClient::ParseURL(const std::string &strRemoteFile) const { // boost::to_upper(strUri); std::transform(strUri.begin(), strUri.end(), strUri.begin(), ::toupper); - if (strUri.compare(0, 3, "FTP") != 0 && strUri.compare(0, 4, "SFTP") != 0) { + if (strUri.compare(0, 4, "FTP:") != 0 && strUri.compare(0, 5, "SFTP:") != 0) { switch (m_eFtpProtocol) { case FTP_PROTOCOL::FTP: case FTP_PROTOCOL::FTPES: