Browse Source

V1.6.8
1、完成了对SFTP的兼容,上传下载还未测试

Apple 5 days ago
parent
commit
560a52e47a
3 changed files with 248 additions and 235 deletions
  1. 208 220
      module/CurlFtp/CurlFtp.cpp
  2. 16 15
      module/CurlFtp/CurlFtp.h
  3. 24 0
      module/CurlFtp/CurlFtpInfo.h

+ 208 - 220
module/CurlFtp/CurlFtp.cpp

@@ -437,7 +437,7 @@ const std::regex parseReg4(R"(^-.*$)");
  */
 static bool parseFileInfo(const std::string& strSrc, std::vector<CF_FileInfo>& fileInfo)
 {
-    SPDLOG_DEBUG("SRC FILE INFO:\n{}", strSrc);
+    // SPDLOG_DEBUG("SRC FILE INFO:\n{}", strSrc);
 #if defined(_WIN32)
     // auto str1 = GBToUTF8(strSrc.c_str());
     std::string str2 = strSrc;
@@ -459,35 +459,42 @@ static bool parseFileInfo(const std::string& strSrc, std::vector<CF_FileInfo>& f
     // std::regex reg3(R"(^d.*)");
     // /* 匹配文件 */
     // std::regex reg4(R"(^-.*$)");
-
-    /* -1 表示对正则表达式之前的子序列感兴趣
-     * 0 表示对正则表达式本身感兴趣,输出的就是换行符 */
-    std::sregex_token_iterator it2(str2.begin(), str2.end(), parseReg2, -1);
-    /* 这里取出每一行 */
-    for(; it2 != std::sregex_token_iterator(); ++it2)
-    {
-        /* 去掉多余的空格 */
-        auto line = std::regex_replace(it2->str(), parseRegSpace, " ");
-        // LOG_INFO("{}", line);
-        CF_FileInfo fi;
-        /* 取出文件类型 */
-        std::string strFileType = std::regex_replace(line, parseReg1, "$1");
-        if(std::regex_match(strFileType, parseReg3))
-        {
-            fi.type = CF_FileType::DIR;
-        }else if (std::regex_match(strFileType, parseReg4))
+    try
+    {
+        /* -1 表示对正则表达式之前的子序列感兴趣
+        * 0 表示对正则表达式本身感兴趣,输出的就是换行符 */
+        std::sregex_token_iterator it2(str2.begin(), str2.end(), parseReg2, -1);
+        /* 这里取出每一行 */
+        for(; it2 != std::sregex_token_iterator(); ++it2)
         {
-            fi.type = CF_FileType::FILE;
+            /* 去掉多余的空格 */
+            auto line = std::regex_replace(it2->str(), parseRegSpace, " ");
+            // LOG_INFO("{}", line);
+            CF_FileInfo fi;
+            /* 取出文件类型 */
+            std::string strFileType = std::regex_replace(line, parseReg1, "$1");
+            if(std::regex_match(strFileType, parseReg3))
+            {
+                fi.type = CF_FileType::DIR;
+            }else if (std::regex_match(strFileType, parseReg4))
+            {
+                fi.type = CF_FileType::FILE;
+            }
+            /* 取出文件大小 */
+            std::string strFileSize = std::regex_replace(line, parseReg1, "$5");
+            fi.size = std::stoull(strFileSize);
+            /* 取出文件名 */
+            std::string strFileName = std::regex_replace(line, parseReg1, "$7");
+            fi.name = strFileName;
+            /* 去掉本地目录和上一级目录 */
+            if(strFileName == "." || strFileName == "..")
+            {
+                continue;
+            }
+            /* 加入队列 */
+            fileInfo.push_back(fi);
         }
-        /* 取出文件大小 */
-        std::string strFileSize = std::regex_replace(line, parseReg1, "$5");
-        fi.size = std::stoull(strFileSize);
-        /* 取出文件名 */
-        std::string strFileName = std::regex_replace(line, parseReg1, "$7");
-        fi.name = strFileName;
-        /* 加入队列 */
-        fileInfo.push_back(fi);
-    }
+    }StdCatch
 
     return true;
 }
@@ -573,16 +580,14 @@ bool CurlFtp::setSftpIPAndPort(const std::string& IP, const int port)
     return true;
 }
 
-/* 设置是否忽略SSL证书,使用SFTP不建议忽略 */
-void CurlFtp::setIgnoreSSLCert(bool isIgnore)
+/* 设置SFTP密钥,仅对SFTP生效 */
+bool CurlFtp::setSftpPrivateKey(const std::string& privateKeyFile, const std::string& publicKeyFile, const std::string& passphrase)
 {
-    m_isIgnoreSSLCert = isIgnore;
-}
+    m_sftpPrivateKeyFile = privateKeyFile;
+    m_sftpPublicKeyFile = publicKeyFile;
+    m_sftpPassphrase = passphrase;
 
-/* 设置CA证书文件 */
-void CurlFtp::setCaCertFile(const std::string& caCertFile)
-{
-    m_caCertFile = caCertFile;
+    return true;
 }
 
 /* 设置是否启用CURL的调试信息 */
@@ -600,11 +605,8 @@ bool CurlFtp::getList(std::string dir, std::vector<CF_FileInfo>& fileInfoList)
         LOG_WARN("IP or port is empty");
         return false;
     }
-
-    resetCurl(m_curl);
-
     /* 获取文件信息 */
-    listByWildcard(m_curl, dir, fileInfoList);
+    listByWildcard(dir, fileInfoList);
 
     // curl_easy_cleanup(curl);
     
@@ -624,7 +626,7 @@ bool CurlFtp::getFileList(std::string dir, std::vector<std::string>& fileList)
 
     std::vector<CF_FileInfo> listInfo;
     /* 获取文件信息 */
-    listByWildcard(m_curl, dirTmp, listInfo);
+    listByWildcard(dirTmp, listInfo);
     for(const CF_FileInfo& fi : listInfo)
     {
         // LOG_INFO("type = {}, size = {}, name = {}", (int)fi.type, fi.size, fi.name);
@@ -650,18 +652,9 @@ bool CurlFtp::getDirList(std::string dir, std::vector<std::string>& dirList)
     /* 检查dir,添加前缀“/” */
     auto dirTmp = checkDirPath(dir);
 
-    // CURL *curl = nullptr;
-    // curl = curl_easy_init();
-    // if(curl == nullptr)
-    // {
-    //     LOG_ERROR("curl init failed !");
-    //     return false;
-    // }
-    resetCurl(m_curl);
-
     std::vector<CF_FileInfo> listInfo;
     /* 获取文件信息 */
-    listByWildcard(m_curl, dirTmp, listInfo);
+    listByWildcard(dirTmp, listInfo);
     for(const CF_FileInfo& fi : listInfo)
     {
         // LOG_INFO("type = {}, size = {}, name = {}", (int)fi.type, fi.size, fi.name);
@@ -764,69 +757,45 @@ bool CurlFtp::deleteFile(const std::string& remoteFile)
     {
         return false;
     }
-
-    /* 切到父目录后再删除基名(兼容更多服务器) */
-    std::string parentDir, fileName;
-    splitParentAndBasePath(filePath, parentDir, fileName);
-    if(fileName.empty())
-    {
-        LOG_ERROR("Delete file base name is empty: " << filePath);
-        return false;
-    }
-
-    std::string parentUrl = m_ftpUrl + parentDir; // parentDir 以'/'结尾
-    if(!setCommonCurlOptions(m_curl, parentUrl, 10))
-    {
-        return false;
-    }
-
+    
     struct curl_slist *headerlist = nullptr;
     std::string deleteCmd;
-    if(m_isSftp)
-    {
-        deleteCmd = "rm " + fileName;   // sftp 使用 rm
-    }
-    else
+    std::string ftpUrl;
+    if(m_isSftp == false)
     {
+        /* 切到父目录后再删除基名(兼容更多服务器) */
+        std::string parentDir, fileName;
+        splitParentAndBasePath(filePath, parentDir, fileName);
+        if(fileName.empty())
+        {
+            LOG_ERROR("Delete file base name is empty: " << filePath);
+            return false;
+        }
+        ftpUrl = m_ftpUrl + parentDir; // parentDir 以'/'结尾
         deleteCmd = "DELE " + fileName; // ftp 使用 DELE
-    }
-    headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
-    curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
-    curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
-    LOG_DEBUG("Delete remote file: " << filePath << ", parent: " << parentDir);
-    bool ret = performCurl(m_curl);
-    curl_slist_free_all(headerlist);
-    if(ret)
-    {
-        return true;
-    }
-
-    /* 回退:尝试使用绝对路径删除(有些服务器允许) */
-    LOG_WARN("Delete by basename failed, try absolute path: " << filePath);
-    std::string rootUrl = m_ftpUrl + "/";
-    if(!setCommonCurlOptions(m_curl, rootUrl, 10))
-    {
-        return false;
-    }
-    headerlist = nullptr;
-    if(m_isSftp)
+    }else 
     {
+        /* SFTP需要使用绝对路径删除 */
+        ftpUrl = m_ftpUrl + "/";
+        headerlist = nullptr;
         deleteCmd = "rm " + filePath;
     }
-    else
+    if(!setCommonCurlOptions(ftpUrl, 10))
     {
-        deleteCmd = "DELE " + filePath;
+        return false;
     }
     headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
     curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
     curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
-    bool ret2 = performCurl(m_curl);
+    LOG_DEBUG("Delete remote file: " << filePath);
+    bool ret = performCurl();
     curl_slist_free_all(headerlist);
-    if(!ret2)
+    if(ret)
     {
-        LOG_ERROR("Failed to delete remote file: " << filePath);
+        return true;
     }
-    return ret2;
+
+    
 }
 
 /* 删除文件夹,递归删除 */
@@ -849,11 +818,11 @@ bool CurlFtp::deleteDirectory(const std::string& ftpDir)
     /* 递归删除:先删除内部所有文件和子目录,再删除自身 */
     std::vector<CF_FileInfo> vecChildren;
     // 使用通配符列举该目录下的所有条目
-    if(!listByWildcard(m_curl, dirWithSlash, vecChildren))
+    if(!listByWildcard(dirWithSlash, vecChildren))
     {
         LOG_WARN("List children failed before delete: " << dirWithSlash);
     }
-    // 先删文件, 先切换到该目录,避免某些服务器删除失败
+    /* 删除文件, 先切换到该目录,避免FTP频繁切换目录 */
     if(!changeToDir(dirWithSlash))
     {
         LOG_ERROR("ChangeToDir failed: " << dirWithSlash);
@@ -868,10 +837,15 @@ bool CurlFtp::deleteDirectory(const std::string& ftpDir)
             {
                 continue;
             }
-            if(!deleteFileRelative(fi.name))
+            if(m_isSftp == false)
             {
-                LOG_ERROR("Failed to delete file: " << fi.name);
-                // 回退:尝试使用绝对路径删除
+                if(!deleteFileRelative(fi.name))
+                {
+                    return false;
+                }
+            }else 
+            {
+                /* SFTP使用绝对路径删除 */
                 std::string absFile = dirWithSlash + fi.name;
                 if(!deleteFile(absFile))
                 {
@@ -905,52 +879,53 @@ bool CurlFtp::deleteDirectory(const std::string& ftpDir)
     }
 
     /* 删除自身目录,走到这一步,这个文件夹内的所有文件都被删除了 */
-    std::string parentDir, baseName;
-    splitParentAndBasePath(dirNoSlash, parentDir, baseName);
-    if(baseName.empty())
-    {
-        LOG_ERROR("Delete directory base name is empty: " << dirNoSlash);
-        return false;
-    }
-    /* 切换到父文件夹并删除当前文件夹 */
-    if(!changeToDir(parentDir))
-    {
-        LOG_ERROR("ChangeToDir parent failed: " << parentDir);
-        return false;
-    }
-    if(deleteDirectoryRelative(baseName))
-    {
-        return true;
-    }
-
-    /* 绝对路径删除回退(部分服务器支持) */
+    struct curl_slist *headerlist = nullptr;
+    std::string deleteCmd;
+    if(m_isSftp == false)
     {
-        std::string rootUrl = m_ftpUrl + "/";
-        if(!setCommonCurlOptions(m_curl, rootUrl, 10))
+        std::string parentDir, baseName;
+        splitParentAndBasePath(dirNoSlash, parentDir, baseName);
+        if(baseName.empty())
         {
+            LOG_ERROR("Delete directory base name is empty: " << dirNoSlash);
             return false;
         }
-        struct curl_slist *headerlist = nullptr;
-        std::string deleteCmd;
-        if(m_isSftp)
+        /* 切换到父文件夹并删除当前文件夹 */
+        if(!changeToDir(parentDir))
         {
-            deleteCmd = std::string("rmdir ") + dirNoSlash;
+            LOG_ERROR("ChangeToDir parent failed: " << parentDir);
+            return false;
         }
-        else
+        // 相对删除:要求已通过 changeToDir() 进入父目录
+        if(baseName.empty())
         {
-            deleteCmd = std::string("RMD ") + dirNoSlash;
+            LOG_ERROR("Delete relative dir name is empty");
+            return false;
         }
-        headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
-        curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
-        curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
-        bool ret2 = performCurl(m_curl);
-        curl_slist_free_all(headerlist);
-        if(!ret2)
+        // 不允许包含路径分隔符
+        if(baseName.find('/') != std::string::npos)
         {
-            LOG_ERROR("Failed to delete remote directory: " << dirNoSlash);
+            LOG_ERROR("DeleteDirectoryRelative only accepts basename without '/' : " << baseName);
+            return false;
         }
-        return ret2;
+        deleteCmd = "RMD " + baseName; // ftp 使用 RMD
+    }else 
+    {
+        /* SFTP使用绝对路径删除 */
+        std::string rootUrl = m_ftpUrl + "/";
+        if(!setCommonCurlOptions(rootUrl, 10))
+        {
+            return false;
+        }
+        deleteCmd = std::string("rmdir ") + dirNoSlash;
     }
+    headerlist = curl_slist_append(headerlist, deleteCmd.c_str());
+    curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
+    curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
+    LOG_DEBUG("Delete relative directory: " << dirNoSlash);
+    bool ret = performCurl();
+    curl_slist_free_all(headerlist);
+    return ret;
 }
 
 
@@ -1008,7 +983,7 @@ bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& loc
     }
 
     /* 设置FTP地址 */
-    setCommonCurlOptions(m_curl, ftpUrl, timeout);
+    setCommonCurlOptions(ftpUrl, timeout);
     
     /* 设置回调函数 */
     curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeFileCallBack_QT);
@@ -1020,7 +995,7 @@ bool CurlFtp::downloadFile(const std::string& remoteFile, const std::string& loc
     curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
     
     /* 发送请求 */
-    bool ret = performCurl(m_curl);
+    bool ret = performCurl();
     if(!ret)
     {
         std::stringstream ss;
@@ -1057,7 +1032,7 @@ bool CurlFtp::downloadToArray(const std::string& remoteFile, std::vector<char>&
 
 
     /* 设置FTP地址 */
-    setCommonCurlOptions(m_curl, ftpUrl, timeout);
+    setCommonCurlOptions(ftpUrl, timeout);
 
     /* 设置回调函数 */
     curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeDataCallBack);
@@ -1069,7 +1044,7 @@ bool CurlFtp::downloadToArray(const std::string& remoteFile, std::vector<char>&
     curl_easy_setopt(m_curl, CURLOPT_NOPROGRESS, 0L);
 
     /* 发送请求 */
-    bool ret = performCurl(m_curl);
+    bool ret = performCurl();
     if(!ret)
     {
         LOG_ERROR("Failed to get file list, Url = " << ftpUrl);
@@ -1151,7 +1126,7 @@ bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remote
 
 
     /* 设置FTP地址 */
-    if(!setCommonCurlOptions(m_curl, ftpUrl, timeout))
+    if(!setCommonCurlOptions(ftpUrl, timeout))
     {
         ifs.close();
         return false;
@@ -1171,7 +1146,7 @@ bool CurlFtp::uploadFile(const std::string& localFile, const std::string& remote
 
     // printf("uploadFile: %d\n", __LINE__);
     /* 发送请求 */
-    bool ret = performCurl(m_curl);
+    bool ret = performCurl();
     if(!ret)
     {
         LOG_ERROR("Upload file failed, Url = " << ftpUrl);
@@ -1237,7 +1212,7 @@ bool CurlFtp::uploadData(char* srcData, size_t size, const std::string& remoteFi
 
 
     /* 设置FTP地址 */
-    if(!setCommonCurlOptions(m_curl, ftpUrl, timeout))
+    if(!setCommonCurlOptions(ftpUrl, timeout))
     {
         return false;
     }
@@ -1263,7 +1238,7 @@ bool CurlFtp::uploadData(char* srcData, size_t size, const std::string& remoteFi
         curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
     }
     /* 发送请求 */
-    bool ret = performCurl(m_curl);
+    bool ret = performCurl();
     if(!ret)
     {
         LOG_ERROR("Upload file failed, Url = " << ftpUrl);
@@ -1340,7 +1315,7 @@ bool CurlFtp::uploadData(char* srcData, size_t size, const std::string& remoteFi
  * @return true 
  * @return false 
  */
-bool CurlFtp::listByWildcard(CURL* curl, const std::string& dir, std::vector<CF_FileInfo>& out)
+bool CurlFtp::listByWildcard(const std::string& dir, std::vector<CF_FileInfo>& out)
 {
     if(m_IP.empty())
     {
@@ -1350,38 +1325,63 @@ bool CurlFtp::listByWildcard(CURL* curl, const std::string& dir, std::vector<CF_
 
     /* 规范化目录并追加通配符 */
     std::string dirWithSlash = checkDirPath(dir);   // 保证以'/'结束
-    std::string ftpUrl = m_ftpUrl + dirWithSlash + "*";
+    std::string ftpUrl;
 
-    if(!setCommonCurlOptions(curl, ftpUrl, 10))
+    if(m_isSftp)
+    {
+        ftpUrl = m_ftpUrl + dirWithSlash;
+    }else {
+        ftpUrl = m_ftpUrl + dirWithSlash + "*";
+    }
+
+    if(!setCommonCurlOptions(ftpUrl, 10))
     {
         return false;
     }
 
+    
     /* 启用通配符匹配并注册回调,仅收集信息不下载 */
-    curl_easy_setopt(m_curl, CURLOPT_WILDCARDMATCH, 1L);
-    curl_easy_setopt(m_curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn_cb);
-    curl_easy_setopt(m_curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end_cb);
-    curl_easy_setopt(m_curl, CURLOPT_CHUNK_DATA, &out);
-
-    // FTP 文本模式对目录兼容性更好(SFTP 忽略该设置)
     if(m_isSftp == false)
     {
+        /* 设置为文本传输 */
         curl_easy_setopt(m_curl, CURLOPT_TRANSFERTEXT, 1L);
+        curl_easy_setopt(m_curl, CURLOPT_WILDCARDMATCH, 1L);
+        curl_easy_setopt(m_curl, CURLOPT_CHUNK_BGN_FUNCTION, chunk_bgn_cb);
+        curl_easy_setopt(m_curl, CURLOPT_CHUNK_END_FUNCTION, chunk_end_cb);
+        curl_easy_setopt(m_curl, CURLOPT_CHUNK_DATA, &out);
+        bool ret = performCurl();
+        if(!ret)
+        {
+            LOG_ERROR("Wildcard list failed, Url = " << ftpUrl);
+        }
+        return ret;
+    }else 
+    {
+        /* 设置回调函数 */
+        std::string strSrc;
+        curl_easy_setopt(m_curl, CURLOPT_WRITEFUNCTION, writeStringCallback);
+        /* 设置需要写入的容器,回调函数的第四个参数 */
+        curl_easy_setopt(m_curl, CURLOPT_WRITEDATA, &strSrc);
+        /* 发送请求 */
+        bool ret = performCurl();
+        if(!ret)
+        {
+            LOG_ERROR("Failed to get file list, Url = " << ftpUrl);
+            return false;
+        }
+        // SPDLOG_DEBUG("SFTP Src string: {}", strSrc);
+        /* 解析字符串 */
+        parseFileInfo(strSrc, out);
+        return true;
     }
-
-    bool ret = performCurl(m_curl);
-    if(!ret)
-    {
-        LOG_ERROR("Wildcard list failed, Url = " << ftpUrl);
-    }
-    return ret;
+    
 }
 
 
 /* 设置常规通用的curl设置 */
-bool CurlFtp::setCommonCurlOptions(CURL* curl, const std::string& ftpUrl, size_t timeout)
+bool CurlFtp::setCommonCurlOptions(const std::string& ftpUrl, size_t timeout)
 {
-    if(!resetCurl(m_curl))
+    if(!resetCurl())
     {
         return false;
     }
@@ -1406,7 +1406,7 @@ bool CurlFtp::setCommonCurlOptions(CURL* curl, const std::string& ftpUrl, size_t
         curl_easy_setopt(m_curl, CURLOPT_VERBOSE, 1L);
     }
     /* 设置SFTP */
-    if(!setSftp(m_curl))
+    if(!setSftp())
     {
         return false;
     }
@@ -1534,12 +1534,12 @@ bool CurlFtp::checkLocalFileExist(const std::string& localFile)
 
 
 /* 执行curl,添加重试机制 */
-bool CurlFtp::performCurl(CURL* curl)
+bool CurlFtp::performCurl()
 {
-    int retry = 3;
+    int retry = 1;
     while(retry > 0)
     {
-        CURLcode res = curl_easy_perform(curl);
+        CURLcode res = curl_easy_perform(m_curl);
         if(res == CURLE_OK)
         {
             return true;
@@ -1548,8 +1548,8 @@ bool CurlFtp::performCurl(CURL* curl)
         {
             LOG_ERROR("Login failed, error code: " << (int)res << ", " << curl_easy_strerror(res));
             /* 设置用户名和密码 */
-            curl_easy_setopt(curl, CURLOPT_USERNAME, m_username.c_str());
-            curl_easy_setopt(curl, CURLOPT_PASSWORD, m_password.c_str());
+            curl_easy_setopt(m_curl, CURLOPT_USERNAME, m_username.c_str());
+            curl_easy_setopt(m_curl, CURLOPT_PASSWORD, m_password.c_str());
         } 
         else if(res == CURLE_REMOTE_FILE_NOT_FOUND)
         {
@@ -1566,8 +1566,19 @@ bool CurlFtp::performCurl(CURL* curl)
     return false;
 }
 
+bool CurlFtp::performCurlStrict()
+{
+    int retry = 1;
+    while (retry-- > 0) {
+        CURLcode res = curl_easy_perform(m_curl);
+        if (res == CURLE_OK) return true;
+        LOG_ERROR("Strict perform failed, code: " << (int)res << ", " << curl_easy_strerror(res));
+    }
+    return false;
+}
+
 /* 重置curl设置 */
-bool CurlFtp::resetCurl(CURL* curl)
+bool CurlFtp::resetCurl()
 {
     if(m_curl == nullptr)
     {
@@ -1584,22 +1595,27 @@ bool CurlFtp::resetCurl(CURL* curl)
 }
 
 /* 设置sftp,如果是sftp就设置 */
-bool CurlFtp::setSftp(CURL* curl)
+bool CurlFtp::setSftp()
 {
     /* 判断是否是SFTP */
-    if(m_isSftp)
+    if(m_isSftp == false)
     {
-        /* 判断是否需要设置CA证书 */
-        if(m_isIgnoreSSLCert)
-        {
-            /* 忽略证书验证 */
-            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
-            /* 忽略主机名验证 */
-            curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
-        } else
+        return true;
+    }
+    /* 设置仅使用密码验证,不优先验证密钥 */
+    if(m_isSftpEnableKey == false)
+    {
+        curl_easy_setopt(m_curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PASSWORD);
+    } else {
+        /* 设置使用密钥验证 */
+        curl_easy_setopt(m_curl, CURLOPT_SSH_AUTH_TYPES, CURLSSH_AUTH_PUBLICKEY);
+        /* 设置公钥和私钥路径 */
+        curl_easy_setopt(m_curl, CURLOPT_SSH_PUBLIC_KEYFILE, m_sftpPublicKeyFile.c_str());
+        curl_easy_setopt(m_curl, CURLOPT_SSH_PRIVATE_KEYFILE, m_sftpPrivateKeyFile.c_str());
+        /* 如果密钥有密码,则设置密码 */
+        if(!m_sftpPassphrase.empty())
         {
-            /* 设置CA证书 */
-            curl_easy_setopt(curl, CURLOPT_CAINFO, m_caCertFile.c_str());
+            curl_easy_setopt(m_curl, CURLOPT_KEYPASSWD, m_sftpPassphrase.c_str());
         }
     }
     return true;
@@ -1617,38 +1633,10 @@ bool CurlFtp::checkFtpDirExist(const std::string& dir)
 {
     /* 检查传入的文件夹 */
     auto dirTmp = checkDirPath(dir);
-    // dirTmp = m_ftpUrl + dirTmp;
-    // if(!setCommonCurlOptions(m_curl, dirTmp, 10))
-    // {
-    //     return false;
-    // }
-//     /* 获取文件夹是否存在,不需要接收文件 */
-//     curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
-//     // curl_easy_setopt(m_curl, CURLOPT_HEADER, 1L);
-
-//     /* 启用持久连接 */
-//     // curl_easy_setopt(m_curl, CURLOPT_TCP_KEEPALIVE, 1L);
-//     /* 这里只需要执行一次 */
-//     CURLcode res = curl_easy_perform(m_curl);
-//     bool result = true;
-//     if(res == CURLE_REMOTE_FILE_NOT_FOUND)
-//     {
-//         result = false;
-//     }
-//     else if(res == CURLE_OK)
-//     {
-//         result = true;
-//     } else 
-//     {
-//         LOG_ERROR("Check remote dir error, error code: " << (int)res << ", " << curl_easy_strerror(res));
-//         result = false;
-//     }
-//     // LOG_DEBUG("Check remote dir: {}, res: {}", dir, (int)res);
-
     /* 获取文件夹 */
     std::string parentDir = QFileInfo(QString::fromStdString(dir)).path().toStdString();
     std::vector<CF_FileInfo> vecDir;
-    bool ret = listByWildcard(m_curl, parentDir, vecDir);
+    bool ret = listByWildcard(parentDir, vecDir);
     if(!ret)
     {
         LOG_ERROR("Failed to check ftp dir: " << dir);
@@ -1709,7 +1697,7 @@ bool CurlFtp::checkFtpFileExist(const std::string& file)
     /* 取出上一层文件夹路径 */
     std::string parentDir = QFileInfo(QString::fromStdString(file)).path().toStdString();
     std::vector<CF_FileInfo> vecFiles;
-    bool ret = listByWildcard(m_curl, parentDir, vecFiles);
+    bool ret = listByWildcard(parentDir, vecFiles);
     if(!ret)
     {
         LOG_ERROR("Failed to check ftp file: " << file);
@@ -1744,13 +1732,13 @@ bool CurlFtp::changeToDir(const std::string& dir)
     std::string url = m_ftpUrl + dirWithSlash;
 
     // 设置到该目录并发送一个不传输实体的请求,相当于验证可进入
-    if(!setCommonCurlOptions(m_curl, url, 10))
+    if(!setCommonCurlOptions(url, 10))
     {
         return false;
     }
     curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
     LOG_DEBUG("Change to remote dir: " << dirWithSlash);
-    return performCurl(m_curl);
+    return performCurlStrict();
 }
 
 /* 删除一个文件,相对路径,调用这个函数,需要先调用上面那个函数进入路径 */
@@ -1786,7 +1774,7 @@ bool CurlFtp::deleteFileRelative(const std::string& remoteFile)
     curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
     curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
     LOG_DEBUG("Delete relative file: " << remoteFile);
-    bool ret = performCurl(m_curl);
+    bool ret = performCurl();
     curl_slist_free_all(headerlist);
     return ret;
 }
@@ -1824,7 +1812,7 @@ bool CurlFtp::deleteDirectoryRelative(const std::string& ftpDir)
     curl_easy_setopt(m_curl, CURLOPT_QUOTE, headerlist);
     curl_easy_setopt(m_curl, CURLOPT_NOBODY, 1L);
     LOG_DEBUG("Delete relative directory: " << ftpDir);
-    bool ret = performCurl(m_curl);
+    bool ret = performCurl();
     curl_slist_free_all(headerlist);
     return ret;
 }
@@ -1850,7 +1838,7 @@ bool CurlFtp::createOneDirectory(const std::string& ftpDir)
         return true;
     }
 
-    setCommonCurlOptions(m_curl, m_ftpUrl, 30);
+    setCommonCurlOptions(m_ftpUrl, 30);
 
     /* 检查传入的文件夹格式 */
     auto dirTmp = checkDirPath(ftpDir);
@@ -1874,7 +1862,7 @@ bool CurlFtp::createOneDirectory(const std::string& ftpDir)
 
     LOG_DEBUG("Create remote dir: " << dirTmp);
     // CURLcode res = curl_easy_perform(curl);
-    bool ret = performCurl(m_curl);
+    bool ret = performCurl();
     if(!ret)
     {
         LOG_ERROR("Failed to create remote Dir");

+ 16 - 15
module/CurlFtp/CurlFtp.h

@@ -31,15 +31,15 @@ public:
 
     /*********************** 成员函数 *********************/
     /* 设置用户名和密码 */
-    bool setUsernameAndPassword(const std::string& username, const std::string& password);
+    bool setUsernameAndPassword(const std::string& username, const std::string& password = "");
     /* 设置IP和端口 */
     bool setFtpIPAndPort(const std::string& IP, const int port = 21);
     /* 设置SFTP的IP和端口 */
     bool setSftpIPAndPort(const std::string& IP, const int port = 22);
-    /* 设置是否忽略SSL证书,使用SFTP不建议忽略 */
-    void setIgnoreSSLCert(bool isIgnore);
-    /* 设置CA证书文件 */
-    void setCaCertFile(const std::string& caCertFile);
+    /* 设置SFTP密钥,仅对SFTP生效 */
+    bool setSftpPrivateKey(const std::string& privateKeyFile, const std::string& publicKeyFile = "", const std::string& passphrase = "");
+    /* 设置启用SFTP密钥,仅对SFTP生效 */
+    void setSftpEnableKey(bool isEnable) { m_isSftpEnableKey = isEnable; }
     /* 设置是否启用CURL的调试信息 */
     void enableCurlDebug(bool isPrint = false);
 
@@ -70,11 +70,11 @@ public:
 
 private:
     /* 设置常规通用的curl设置 */
-    bool setCommonCurlOptions(CURL* curl, const std::string& ftpUrl, size_t timeout);
+    bool setCommonCurlOptions(const std::string& ftpUrl, size_t timeout);
     /* 列出所有内容 */
     // bool listAll(CURL* curl, std::string dir, std::vector<CF_FileInfo>& fileInfoList);
-    /* 通过通配符列出文件信息 */
-    bool listByWildcard(CURL* curl, const std::string& dir, std::vector<CF_FileInfo>& out);
+    /* 通过通配符列出文件信息,SFTP依旧是使用正则表达式解析字符串 */
+    bool listByWildcard(const std::string& dir, std::vector<CF_FileInfo>& out);
     /* 检查FTP文件夹路径是否合规,不合规就修改 */
     std::string checkDirPath(const std::string& dir);
     /* 检查FTP文件路径是否合规 */
@@ -86,15 +86,14 @@ private:
     /* 检查本地文件是否存在 */
     bool checkLocalFileExist(const std::string& localFile);
     /* 执行curl,添加重试机制 */
-    bool performCurl(CURL* curl);
+    bool performCurl();
+    bool performCurlStrict();
     /* 重置curl设置 */
-    inline bool resetCurl(CURL* curl);
+    inline bool resetCurl();
     /* 设置sftp,如果是sftp就设置 */
-    inline bool setSftp(CURL* curl);
+    inline bool setSftp();
     /* 检查FTP文件夹是否存在 */
     bool checkFtpDirExist(const std::string& dir);
-    /* 检查SFTP文件夹是否存在 */
-    // bool checkSftpDirExist(const std::string& dir);
     /* 检查FTP/SFTP文件是否存在,使用通配符获取文件列表匹配 */
     bool checkFtpFileExist(const std::string& file);
     /* 进入一个路径 */
@@ -119,10 +118,12 @@ private:
     std::string m_password;
 
     bool m_isSftp = false;                  /* 是否是SFTP */
-    bool m_isIgnoreSSLCert = false;         /* 是否忽略SSL证书 */
     bool m_enableCurlDebug = false;         /* 是否打印调试信息 */
 
-    std::string m_caCertFile;               /* CA证书文件 */
+    bool m_isSftpEnableKey = false;         /* 是否启用SFTP密钥,默认不启用 */
+    std::string m_sftpPrivateKeyFile;       /* SFTP私钥文件 */
+    std::string m_sftpPublicKeyFile;        /* SFTP公钥文件 */
+    std::string m_sftpPassphrase;           /* SFTP密钥密码 */
 };
 
 

+ 24 - 0
module/CurlFtp/CurlFtpInfo.h

@@ -6,6 +6,30 @@
 
 
 
+/* Std 异常处理的宏 */
+#define StdCatch \
+    catch (const std::exception& e)\
+    {\
+        SPDLOG_WARN("Std std failed: {}", e.what());\
+        return false;\
+    }catch(...)\
+    {\
+        SPDLOG_WARN("Std Operated failed: unknown error");\
+        return false;\
+    }
+
+/* Std 异常处理的宏,不带返回值 */
+#define StdCatchNoReturn \
+    catch (const std::exception& e)\
+    {\
+        SPDLOG_WARN("Std std failed: {}", e.what());\
+        return;\
+    }catch(...)\
+    {\
+        SPDLOG_WARN("Std Operated failed: unknown error");\
+        return;\
+    }
+