/*============================================================================ CMake - Cross Platform Makefile Generator Copyright 2000-2015 Kitware, Inc. Distributed under the OSI-approved BSD License (the "License"); see accompanying file Copyright.txt for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the License for more information. ============================================================================*/ #include "cmCTestCurl.h" #include "cmCTest.h" #include "cmSystemTools.h" cmCTestCurl::cmCTestCurl(cmCTest* ctest) { this->CTest = ctest; this->SetProxyType(); this->UseHttp10 = false; // In windows, this will init the winsock stuff ::curl_global_init(CURL_GLOBAL_ALL); // default is to verify https this->VerifyPeerOff = false; this->VerifyHostOff = false; this->TimeOutSeconds = 0; this->Curl = curl_easy_init(); } cmCTestCurl::~cmCTestCurl() { ::curl_easy_cleanup(this->Curl); ::curl_global_cleanup(); } std::string cmCTestCurl::Escape(std::string const& source) { char* data1 = curl_easy_escape(this->Curl, source.c_str(), 0); std::string ret = data1; curl_free(data1); return ret; } namespace { static size_t curlWriteMemoryCallback(void* ptr, size_t size, size_t nmemb, void* data) { int realsize = (int)(size * nmemb); std::vector* vec = static_cast*>(data); const char* chPtr = static_cast(ptr); vec->insert(vec->end(), chPtr, chPtr + realsize); return realsize; } static size_t curlDebugCallback(CURL*, curl_infotype, char* chPtr, size_t size, void* data) { std::vector* vec = static_cast*>(data); vec->insert(vec->end(), chPtr, chPtr + size); return size; } } void cmCTestCurl::SetCurlOptions(std::vector const& args) { for (std::vector::const_iterator i = args.begin(); i != args.end(); ++i) { if (*i == "CURLOPT_SSL_VERIFYPEER_OFF") { this->VerifyPeerOff = true; } if (*i == "CURLOPT_SSL_VERIFYHOST_OFF") { this->VerifyHostOff = true; } } } bool cmCTestCurl::InitCurl() { if (!this->Curl) { return false; } if (this->VerifyPeerOff) { curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYPEER, 0); } if (this->VerifyHostOff) { curl_easy_setopt(this->Curl, CURLOPT_SSL_VERIFYHOST, 0); } if (this->HTTPProxy.size()) { curl_easy_setopt(this->Curl, CURLOPT_PROXY, this->HTTPProxy.c_str()); curl_easy_setopt(this->Curl, CURLOPT_PROXYTYPE, this->HTTPProxyType); if (this->HTTPProxyAuth.size() > 0) { curl_easy_setopt(this->Curl, CURLOPT_PROXYUSERPWD, this->HTTPProxyAuth.c_str()); } } if (this->UseHttp10) { curl_easy_setopt(this->Curl, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_0); } // enable HTTP ERROR parsing curl_easy_setopt(this->Curl, CURLOPT_FAILONERROR, 1); return true; } bool cmCTestCurl::UploadFile(std::string const& local_file, std::string const& url, std::string const& fields, std::string& response) { response = ""; if (!this->InitCurl()) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Initialization of curl failed"); return false; } /* enable uploading */ curl_easy_setopt(this->Curl, CURLOPT_UPLOAD, 1); // if there is little to no activity for too long stop submitting if (this->TimeOutSeconds) { ::curl_easy_setopt(this->Curl, CURLOPT_LOW_SPEED_LIMIT, 1); ::curl_easy_setopt(this->Curl, CURLOPT_LOW_SPEED_TIME, this->TimeOutSeconds); } /* HTTP PUT please */ ::curl_easy_setopt(this->Curl, CURLOPT_PUT, 1); ::curl_easy_setopt(this->Curl, CURLOPT_VERBOSE, 1); FILE* ftpfile = cmsys::SystemTools::Fopen(local_file, "rb"); if (!ftpfile) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Could not open file for upload: " << local_file << "\n"); return false; } // set the url std::string upload_url = url; upload_url += "?"; upload_url += fields; ::curl_easy_setopt(this->Curl, CURLOPT_URL, upload_url.c_str()); // now specify which file to upload ::curl_easy_setopt(this->Curl, CURLOPT_INFILE, ftpfile); unsigned long filelen = cmSystemTools::FileLength(local_file); // and give the size of the upload (optional) ::curl_easy_setopt(this->Curl, CURLOPT_INFILESIZE, static_cast(filelen)); ::curl_easy_setopt(this->Curl, CURLOPT_WRITEFUNCTION, curlWriteMemoryCallback); ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGFUNCTION, curlDebugCallback); // Be sure to set Content-Type to satisfy fussy modsecurity rules struct curl_slist* headers = ::curl_slist_append(NULL, "Content-Type: text/xml"); ::curl_easy_setopt(this->Curl, CURLOPT_HTTPHEADER, headers); std::vector responseData; std::vector debugData; ::curl_easy_setopt(this->Curl, CURLOPT_FILE, (void*)&responseData); ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGDATA, (void*)&debugData); ::curl_easy_setopt(this->Curl, CURLOPT_FAILONERROR, 1); // Now run off and do what you've been told! ::curl_easy_perform(this->Curl); ::fclose(ftpfile); ::curl_easy_setopt(this->Curl, CURLOPT_HTTPHEADER, NULL); ::curl_slist_free_all(headers); if (responseData.size() > 0) { response = std::string(responseData.begin(), responseData.end()); cmCTestLog(this->CTest, HANDLER_VERBOSE_OUTPUT, "Curl response: [" << response << "]\n"); } std::string curlDebug; if (debugData.size() > 0) { curlDebug = std::string(debugData.begin(), debugData.end()); cmCTestLog(this->CTest, DEBUG, "Curl debug: [" << curlDebug << "]\n"); } if (response.size() == 0) { cmCTestLog(this->CTest, ERROR_MESSAGE, "No response from server.\n" << curlDebug); return false; } return true; } bool cmCTestCurl::HttpRequest(std::string const& url, std::string const& fields, std::string& response) { response = ""; cmCTestLog(this->CTest, DEBUG, "HttpRequest\n" << "url: " << url << "\n" << "fields " << fields << "\n"); if (!this->InitCurl()) { cmCTestLog(this->CTest, ERROR_MESSAGE, "Initialization of curl failed"); return false; } curl_easy_setopt(this->Curl, CURLOPT_POST, 1); curl_easy_setopt(this->Curl, CURLOPT_POSTFIELDS, fields.c_str()); ::curl_easy_setopt(this->Curl, CURLOPT_URL, url.c_str()); ::curl_easy_setopt(this->Curl, CURLOPT_FOLLOWLOCATION, 1); // set response options ::curl_easy_setopt(this->Curl, CURLOPT_WRITEFUNCTION, curlWriteMemoryCallback); ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGFUNCTION, curlDebugCallback); std::vector responseData; std::vector debugData; ::curl_easy_setopt(this->Curl, CURLOPT_FILE, (void*)&responseData); ::curl_easy_setopt(this->Curl, CURLOPT_DEBUGDATA, (void*)&debugData); ::curl_easy_setopt(this->Curl, CURLOPT_FAILONERROR, 1); CURLcode res = ::curl_easy_perform(this->Curl); if (responseData.size() > 0) { response = std::string(responseData.begin(), responseData.end()); cmCTestLog(this->CTest, DEBUG, "Curl response: [" << response << "]\n"); } if (debugData.size() > 0) { std::string curlDebug = std::string(debugData.begin(), debugData.end()); cmCTestLog(this->CTest, DEBUG, "Curl debug: [" << curlDebug << "]\n"); } cmCTestLog(this->CTest, DEBUG, "Curl res: " << res << "\n"); return (res == 0); } void cmCTestCurl::SetProxyType() { if (cmSystemTools::GetEnv("HTTP_PROXY")) { this->HTTPProxy = cmSystemTools::GetEnv("HTTP_PROXY"); if (cmSystemTools::GetEnv("HTTP_PROXY_PORT")) { this->HTTPProxy += ":"; this->HTTPProxy += cmSystemTools::GetEnv("HTTP_PROXY_PORT"); } if (cmSystemTools::GetEnv("HTTP_PROXY_TYPE")) { // this is the default this->HTTPProxyType = CURLPROXY_HTTP; std::string type = cmSystemTools::GetEnv("HTTP_PROXY_TYPE"); // HTTP/SOCKS4/SOCKS5 if (type == "HTTP") { this->HTTPProxyType = CURLPROXY_HTTP; } else if (type == "SOCKS4") { this->HTTPProxyType = CURLPROXY_SOCKS4; } else if (type == "SOCKS5") { this->HTTPProxyType = CURLPROXY_SOCKS5; } } if (cmSystemTools::GetEnv("HTTP_PROXY_USER")) { this->HTTPProxyAuth = cmSystemTools::GetEnv("HTTP_PROXY_USER"); } if (cmSystemTools::GetEnv("HTTP_PROXY_PASSWD")) { this->HTTPProxyAuth += ":"; this->HTTPProxyAuth += cmSystemTools::GetEnv("HTTP_PROXY_PASSWD"); } } }