|
|
#include "curl_utils.h"
|
|
|
#include <chrono>
|
|
|
|
|
|
// 回调函数,用于处理从服务器接收到的数据
|
|
|
size_t WriteCallback(void* contents, size_t size, size_t nmemb, std::string* userp) {
|
|
|
size_t totalSize = size * nmemb;
|
|
|
userp->append((char*)contents, totalSize);
|
|
|
return totalSize;
|
|
|
}
|
|
|
|
|
|
// 回调函数,用于处理接收到的响应头
|
|
|
size_t HeaderCallback(char* buffer, size_t size, size_t nitems, void* userdata) {
|
|
|
HeaderData* headerData = static_cast<HeaderData*>(userdata);
|
|
|
std::string header(buffer, size * nitems);
|
|
|
if (!headerData->found && header.find("x-csrf-token:") == 0) {
|
|
|
size_t colonPos = header.find(':');
|
|
|
if (colonPos != std::string::npos) {
|
|
|
headerData->value = header.substr(colonPos + 1);
|
|
|
headerData->value.erase(0, headerData->value.find_first_not_of(" \t"));
|
|
|
headerData->value.erase(headerData->value.length() - 2);
|
|
|
headerData->found = true; // 标记为已找到
|
|
|
}
|
|
|
}
|
|
|
|
|
|
return size * nitems;
|
|
|
}
|
|
|
|
|
|
vector<string> split_regex(const string& str, const string& pattern) {
|
|
|
vector<string> tokens;
|
|
|
regex rgx(pattern);
|
|
|
sregex_token_iterator iter(str.begin(), str.end(), rgx, -1);
|
|
|
sregex_token_iterator end;
|
|
|
while (iter != end) {
|
|
|
tokens.push_back(*iter++);
|
|
|
}
|
|
|
return tokens;
|
|
|
}
|
|
|
|
|
|
string URLEncodeFTPPath2(const std::string& path) {
|
|
|
CURL* curl = curl_easy_init();
|
|
|
if (!curl) return path;
|
|
|
|
|
|
std::string result;
|
|
|
std::string encoded_part;
|
|
|
|
|
|
// 分割路径,分别编码每个部分
|
|
|
size_t start = 0;
|
|
|
size_t end = path.find('/');
|
|
|
|
|
|
while (end != std::string::npos) {
|
|
|
std::string part = path.substr(start, end - start);
|
|
|
if (!part.empty()) {
|
|
|
char* encoded = curl_easy_escape(curl, part.c_str(), part.length());
|
|
|
if (encoded) {
|
|
|
result += encoded;
|
|
|
curl_free(encoded);
|
|
|
}
|
|
|
else {
|
|
|
result += part;
|
|
|
}
|
|
|
}
|
|
|
result += '/';
|
|
|
start = end + 1;
|
|
|
end = path.find('/', start);
|
|
|
}
|
|
|
|
|
|
// 最后一部分
|
|
|
if (start < path.length()) {
|
|
|
std::string part = path.substr(start);
|
|
|
char* encoded = curl_easy_escape(curl, part.c_str(), part.length());
|
|
|
if (encoded) {
|
|
|
result += encoded;
|
|
|
curl_free(encoded);
|
|
|
}
|
|
|
else {
|
|
|
result += part;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
curl_easy_cleanup(curl);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
// 写入回调函数(用于上传文件)
|
|
|
size_t read_callback(void* ptr, size_t size, size_t nmemb, FILE* stream) {
|
|
|
size_t retcode = fread(ptr, size, nmemb, stream);
|
|
|
return retcode;
|
|
|
}
|
|
|
|
|
|
int sendFileByFtp(string ftp_url, string file_path, string file_dir, string user, string pwd)
|
|
|
{
|
|
|
int result = 0;
|
|
|
CURL* curl;
|
|
|
CURLcode res;
|
|
|
|
|
|
// 初始化libcurl
|
|
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
|
curl = curl_easy_init();
|
|
|
|
|
|
if (curl) {
|
|
|
string encoded_ftp_url = URLEncodeFTPPath2(ftp_url);
|
|
|
// 1. 创建远程目录
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, encoded_ftp_url.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_USERNAME, user.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_PASSWORD, pwd.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR);
|
|
|
|
|
|
// 重要:对于非标准端口,可能需要额外设置
|
|
|
curl_easy_setopt(curl, CURLOPT_FTPPORT, "-"); // 被动模式
|
|
|
curl_easy_setopt(curl, CURLOPT_FTP_USE_EPSV, 0L); // 禁用 EPSV
|
|
|
|
|
|
// 启用详细日志查看具体问题
|
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
|
// 设置超时
|
|
|
curl_easy_setopt(curl, CURLOPT_TIMEOUT, 30L);
|
|
|
curl_easy_setopt(curl, CURLOPT_CONNECTTIMEOUT, 10L);
|
|
|
|
|
|
// 执行目录创建
|
|
|
res = curl_easy_perform(curl);
|
|
|
if (res != CURLE_OK) {
|
|
|
WriteLog("FTP 创建目录失: %s (错误: %d)\n",
|
|
|
curl_easy_strerror(res), res);
|
|
|
result = 1;
|
|
|
goto end;
|
|
|
}
|
|
|
|
|
|
// 2. 上传文件到新目录
|
|
|
FILE* file = nullptr;
|
|
|
errno_t err = fopen_s(&file, file_path.c_str(), "rb");
|
|
|
if (err != 0 || file == nullptr) {
|
|
|
// 处理错误
|
|
|
WriteLog("无法打开本地文件>%s\n", file_path.c_str());
|
|
|
result = 1;
|
|
|
goto end;
|
|
|
}
|
|
|
//FILE* file = fopen(file_path.c_str(), "rb");
|
|
|
//if (!file) {
|
|
|
// WriteLog("无法打开本地文件>%s\n", file_path.c_str());
|
|
|
// result = 1;
|
|
|
// goto end;
|
|
|
//}
|
|
|
|
|
|
// 设置FTP上传选项
|
|
|
curl_easy_setopt(curl, CURLOPT_UPLOAD, 1L);
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, file_dir.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_READFUNCTION, read_callback);
|
|
|
curl_easy_setopt(curl, CURLOPT_READDATA, file);
|
|
|
|
|
|
// 获取文件大小
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
curl_off_t file_size = ftell(file);
|
|
|
fseek(file, 0, SEEK_SET);
|
|
|
curl_easy_setopt(curl, CURLOPT_INFILESIZE_LARGE, file_size);
|
|
|
|
|
|
// 执行文件上传
|
|
|
res = curl_easy_perform(curl);
|
|
|
if (res != CURLE_OK) {
|
|
|
WriteLog("上传文件失败:%s\n", curl_easy_strerror(res));
|
|
|
result = 1;
|
|
|
}
|
|
|
else {
|
|
|
WriteLog("文件上传成功\n");
|
|
|
}
|
|
|
|
|
|
fclose(file);
|
|
|
curl_easy_cleanup(curl);
|
|
|
}
|
|
|
|
|
|
end:
|
|
|
curl_global_cleanup();
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
string postJSON(string url, string body, long& http_code, string& error)
|
|
|
{
|
|
|
string response_data = "";
|
|
|
CURL* curl;
|
|
|
CURLcode res;
|
|
|
// 初始化 libcurl 会话
|
|
|
curl_global_init(CURL_GLOBAL_DEFAULT);
|
|
|
curl = curl_easy_init();
|
|
|
|
|
|
if (curl) {
|
|
|
// 设置要访问的 URL
|
|
|
curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "POST");
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, url.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION, 1L);
|
|
|
curl_easy_setopt(curl, CURLOPT_DEFAULT_PROTOCOL, "http");
|
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, 0L);
|
|
|
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYHOST, 0L);
|
|
|
|
|
|
// 设置请求头
|
|
|
struct curl_slist* headers = NULL;
|
|
|
headers = curl_slist_append(headers, "Content-Type: application/json; charset=UTF-8");
|
|
|
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, headers);
|
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, body.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, body.size());
|
|
|
|
|
|
// 设置回调函数以接收响应数据
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, WriteCallback);
|
|
|
curl_easy_setopt(curl, CURLOPT_WRITEDATA, &response_data);
|
|
|
|
|
|
// 执行请求
|
|
|
res = curl_easy_perform(curl);
|
|
|
|
|
|
// 检查错误
|
|
|
if (res != CURLE_OK)
|
|
|
{
|
|
|
error = curl_easy_strerror(res);
|
|
|
|
|
|
}
|
|
|
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
|
|
|
// 清理资源
|
|
|
curl_slist_free_all(headers);
|
|
|
curl_easy_cleanup(curl);
|
|
|
}
|
|
|
// 清理全局环境
|
|
|
curl_global_cleanup();
|
|
|
|
|
|
return response_data;
|
|
|
}
|
|
|
|
|
|
// URL 编码函数(处理空格等特殊字符)
|
|
|
std::string URLEncodeFTPPath(const std::string& path) {
|
|
|
CURL* curl = curl_easy_init();
|
|
|
if (!curl) return path;
|
|
|
|
|
|
std::string result;
|
|
|
std::string encoded_part;
|
|
|
|
|
|
// 分割路径,分别编码每个部分
|
|
|
size_t start = 0;
|
|
|
size_t end = path.find('/');
|
|
|
|
|
|
while (end != std::string::npos) {
|
|
|
std::string part = path.substr(start, end - start);
|
|
|
if (!part.empty()) {
|
|
|
char* encoded = curl_easy_escape(curl, part.c_str(), part.length());
|
|
|
if (encoded) {
|
|
|
result += encoded;
|
|
|
curl_free(encoded);
|
|
|
}
|
|
|
else {
|
|
|
result += part;
|
|
|
}
|
|
|
}
|
|
|
result += '/';
|
|
|
start = end + 1;
|
|
|
end = path.find('/', start);
|
|
|
}
|
|
|
|
|
|
// 最后一部分
|
|
|
if (start < path.length()) {
|
|
|
std::string part = path.substr(start);
|
|
|
char* encoded = curl_easy_escape(curl, part.c_str(), part.length());
|
|
|
if (encoded) {
|
|
|
result += encoded;
|
|
|
curl_free(encoded);
|
|
|
}
|
|
|
else {
|
|
|
result += part;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
curl_easy_cleanup(curl);
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
bool TestBasicFTPConnection() {
|
|
|
CURL* curl = curl_easy_init();
|
|
|
if (!curl) return false;
|
|
|
|
|
|
// 最简单的测试 - 只连接到根目录
|
|
|
std::string test_url = "ftp://192.168.120.92:22/";
|
|
|
|
|
|
WriteLog("测试基础连接: %s\n", test_url.c_str());
|
|
|
|
|
|
curl_easy_setopt(curl, CURLOPT_URL, test_url.c_str());
|
|
|
curl_easy_setopt(curl, CURLOPT_USERNAME, "ftp1");
|
|
|
curl_easy_setopt(curl, CURLOPT_PASSWORD, "Jd@tc00"); // 需要密码
|
|
|
|
|
|
// 只连接,不执行任何操作
|
|
|
curl_easy_setopt(curl, CURLOPT_NOBODY, 1L);
|
|
|
|
|
|
// 启用详细日志
|
|
|
curl_easy_setopt(curl, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
|
CURLcode res = curl_easy_perform(curl);
|
|
|
|
|
|
if (res != CURLE_OK) {
|
|
|
WriteLog("基础连接失败: %s (错误: %d)\n",
|
|
|
curl_easy_strerror(res), res);
|
|
|
}
|
|
|
else {
|
|
|
WriteLog("基础连接成功\n");
|
|
|
}
|
|
|
|
|
|
curl_easy_cleanup(curl);
|
|
|
return (res == CURLE_OK);
|
|
|
}
|
|
|
|
|
|
bool UploadToFTPWithDebug() {
|
|
|
// 你的参数
|
|
|
std::string server = "192.168.120.92";
|
|
|
int port = 22;
|
|
|
std::string username = "ftp1";
|
|
|
std::string password = "Jd@tc00";
|
|
|
std::string remote_dir = "DocumentRevision/MS WordX/WD00000644-10";
|
|
|
std::string filename = "WD00000644_ceshiwen.docx";
|
|
|
std::string local_file = "C:\\Users\\ADMINI~1\\AppData\\Local\\Temp\\2\\WD00000644_ceshiwen.docx";
|
|
|
|
|
|
WriteLog("=== FTP 上传调试开始 ===\n");
|
|
|
WriteLog("服务器: %s:%d\n", server.c_str(), port);
|
|
|
WriteLog("用户名: %s\n", username.c_str());
|
|
|
WriteLog("本地文件: %s\n", local_file.c_str());
|
|
|
WriteLog("远程目录: %s\n", remote_dir.c_str());
|
|
|
WriteLog("文件名: %s\n", filename.c_str());
|
|
|
|
|
|
// 1. 先测试基础连接
|
|
|
if (!TestBasicFTPConnection()) {
|
|
|
WriteLog("基础连接测试失败,请检查网络和凭据\n");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 2. 创建目录
|
|
|
CURL* curl_dir = curl_easy_init();
|
|
|
if (curl_dir) {
|
|
|
// 构建目录URL - 特别注意空格的处理
|
|
|
std::string dir_path = remote_dir;
|
|
|
|
|
|
// 方法1:直接使用路径(libcurl 会自动处理空格)
|
|
|
std::string dir_url = "ftp://" + server + ":" + std::to_string(port) + "/" + dir_path + "/";
|
|
|
|
|
|
WriteLog("尝试创建目录,URL: %s\n", dir_url.c_str());
|
|
|
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_URL, dir_url.c_str());
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_USERNAME, username.c_str());
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_PASSWORD, password.c_str());
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR);
|
|
|
|
|
|
// 重要:设置 FTP 选项
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_FTPPORT, "-"); // 被动模式
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_FTP_USE_EPSV, 0L); // 禁用 EPSV
|
|
|
|
|
|
// 设置超时
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_TIMEOUT, 30L);
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_CONNECTTIMEOUT, 10L);
|
|
|
|
|
|
// 启用详细日志
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
|
CURLcode res = curl_easy_perform(curl_dir);
|
|
|
|
|
|
if (res != CURLE_OK) {
|
|
|
WriteLog("目录创建失败: %s (错误: %d)\n",
|
|
|
curl_easy_strerror(res), res);
|
|
|
|
|
|
// 尝试另一种方法:手动编码
|
|
|
std::string encoded_dir = URLEncodeFTPPath(dir_path);
|
|
|
std::string dir_url2 = "ftp://" + server + ":" + std::to_string(port) + "/" + encoded_dir + "/";
|
|
|
|
|
|
WriteLog("尝试方法2,编码URL: %s\n", dir_url2.c_str());
|
|
|
|
|
|
// 重置并重试
|
|
|
curl_easy_reset(curl_dir);
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_URL, dir_url2.c_str());
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_USERNAME, username.c_str());
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_PASSWORD, password.c_str());
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR);
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_FTPPORT, "-");
|
|
|
curl_easy_setopt(curl_dir, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
|
res = curl_easy_perform(curl_dir);
|
|
|
|
|
|
if (res != CURLE_OK) {
|
|
|
WriteLog("方法2也失败: %s\n", curl_easy_strerror(res));
|
|
|
}
|
|
|
}
|
|
|
else {
|
|
|
WriteLog("目录创建成功\n");
|
|
|
}
|
|
|
|
|
|
curl_easy_cleanup(curl_dir);
|
|
|
}
|
|
|
|
|
|
// 3. 上传文件
|
|
|
CURL* curl_upload = curl_easy_init();
|
|
|
if (!curl_upload) {
|
|
|
WriteLog("初始化上传CURL失败\n");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 打开本地文件
|
|
|
FILE* file = nullptr;
|
|
|
errno_t err = fopen_s(&file, local_file.c_str(), "rb");
|
|
|
if (err != 0 || file == nullptr) {
|
|
|
// 处理错误
|
|
|
WriteLog("无法打开本地文件>%s\n", local_file.c_str());
|
|
|
curl_easy_cleanup(curl_upload);
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
// 获取文件大小
|
|
|
fseek(file, 0, SEEK_END);
|
|
|
long file_size = ftell(file);
|
|
|
fseek(file, 0, SEEK_SET);
|
|
|
|
|
|
WriteLog("文件大小: %ld bytes\n", file_size);
|
|
|
|
|
|
// 构建上传URL
|
|
|
std::string upload_path = remote_dir + "/" + filename;
|
|
|
std::string upload_url = "ftp://" + server + ":" + std::to_string(port) + "/" + upload_path;
|
|
|
|
|
|
WriteLog("上传URL: %s\n", upload_url.c_str());
|
|
|
|
|
|
// 设置上传选项
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_URL, upload_url.c_str());
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_USERNAME, username.c_str());
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_PASSWORD, password.c_str());
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_UPLOAD, 1L);
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_READDATA, file);
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size);
|
|
|
|
|
|
// FTP 特定选项
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_FTPPORT, "-"); // 被动模式
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_FTP_CREATE_MISSING_DIRS, CURLFTP_CREATE_DIR);
|
|
|
|
|
|
// 设置超时
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_TIMEOUT, 60L);
|
|
|
|
|
|
// 启用详细日志
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
|
// 执行上传
|
|
|
CURLcode res = curl_easy_perform(curl_upload);
|
|
|
|
|
|
// 关闭文件
|
|
|
fclose(file);
|
|
|
|
|
|
if (res != CURLE_OK) {
|
|
|
WriteLog("上传失败: %s (错误: %d)\n",
|
|
|
curl_easy_strerror(res), res);
|
|
|
|
|
|
// 尝试使用编码的URL
|
|
|
std::string encoded_upload_path = URLEncodeFTPPath(upload_path);
|
|
|
std::string upload_url2 = "ftp://" + server + ":" + std::to_string(port) + "/" + encoded_upload_path;
|
|
|
|
|
|
WriteLog("尝试编码URL上传: %s\n", upload_url2.c_str());
|
|
|
|
|
|
|
|
|
//file = fopen(local_file.c_str(), "rb");
|
|
|
// 重新打开文件
|
|
|
errno_t err = fopen_s(&file, local_file.c_str(), "rb");
|
|
|
if (err != 0 || file == nullptr) {
|
|
|
// 处理错误
|
|
|
WriteLog("重新打开文件无法打开本地文件>%s\n", local_file.c_str());
|
|
|
curl_easy_cleanup(curl_upload);
|
|
|
return false;
|
|
|
}
|
|
|
if (file) {
|
|
|
curl_easy_reset(curl_upload);
|
|
|
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_URL, upload_url2.c_str());
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_USERNAME, username.c_str());
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_PASSWORD, password.c_str());
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_UPLOAD, 1L);
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_READDATA, file);
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_INFILESIZE_LARGE, (curl_off_t)file_size);
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_FTPPORT, "-");
|
|
|
curl_easy_setopt(curl_upload, CURLOPT_VERBOSE, 1L);
|
|
|
|
|
|
res = curl_easy_perform(curl_upload);
|
|
|
fclose(file);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
if (res == CURLE_OK) {
|
|
|
WriteLog("=== 文件上传成功 ===\n");
|
|
|
WriteLog("文件已上传到: %s\n", upload_url.c_str());
|
|
|
}
|
|
|
else {
|
|
|
WriteLog("=== 文件上传失败 ===\n");
|
|
|
WriteLog("最终错误: %s\n", curl_easy_strerror(res));
|
|
|
}
|
|
|
|
|
|
curl_easy_cleanup(curl_upload);
|
|
|
return (res == CURLE_OK);
|
|
|
}
|
|
|
|
|
|
void CheckCurlFeatures() {
|
|
|
curl_version_info_data* version_info = curl_version_info(CURLVERSION_NOW);
|
|
|
|
|
|
WriteLog("=== libcurl 功能检查 ===\n");
|
|
|
WriteLog("libcurl 版本: %s\n", version_info->version);
|
|
|
WriteLog("支持的协议:\n");
|
|
|
|
|
|
// 检查协议支持
|
|
|
if (version_info->protocols) {
|
|
|
for (int i = 0; version_info->protocols[i]; i++) {
|
|
|
WriteLog(" - %s\n", version_info->protocols[i]);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// 检查特定协议
|
|
|
bool supports_ftp = false;
|
|
|
bool supports_sftp = false;
|
|
|
|
|
|
for (int i = 0; version_info->protocols[i]; i++) {
|
|
|
if (strcmp(version_info->protocols[i], "ftp") == 0) {
|
|
|
supports_ftp = true;
|
|
|
}
|
|
|
if (strcmp(version_info->protocols[i], "sftp") == 0) {
|
|
|
supports_sftp = true;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
WriteLog("\nFTP 支持: %s\n", supports_ftp ? "是" : "否");
|
|
|
WriteLog("SFTP 支持: %s\n", supports_sftp ? "是" : "否");
|
|
|
|
|
|
if (!supports_ftp && !supports_sftp) {
|
|
|
WriteLog("错误: libcurl 没有编译 FTP/SFTP 支持!\n");
|
|
|
WriteLog("需要重新编译 libcurl 或使用支持 FTP 的版本\n");
|
|
|
}
|
|
|
} |