michael@0: // Copyright (c) 2006, Google Inc. michael@0: // All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions are michael@0: // met: michael@0: // michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above michael@0: // copyright notice, this list of conditions and the following disclaimer michael@0: // in the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google Inc. nor the names of its michael@0: // contributors may be used to endorse or promote products derived from michael@0: // this software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR michael@0: // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT michael@0: // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, michael@0: // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT michael@0: // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, michael@0: // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY michael@0: // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT michael@0: // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE michael@0: // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. michael@0: michael@0: #include "common/linux/http_upload.h" michael@0: michael@0: #include michael@0: #include michael@0: #include "third_party/curl/curl.h" michael@0: michael@0: namespace { michael@0: michael@0: // Callback to get the response data from server. michael@0: static size_t WriteCallback(void *ptr, size_t size, michael@0: size_t nmemb, void *userp) { michael@0: if (!userp) michael@0: return 0; michael@0: michael@0: string *response = reinterpret_cast(userp); michael@0: size_t real_size = size * nmemb; michael@0: response->append(reinterpret_cast(ptr), real_size); michael@0: return real_size; michael@0: } michael@0: michael@0: } // namespace michael@0: michael@0: namespace google_breakpad { michael@0: michael@0: static const char kUserAgent[] = "Breakpad/1.0 (Linux)"; michael@0: michael@0: // static michael@0: bool HTTPUpload::SendRequest(const string &url, michael@0: const map ¶meters, michael@0: const string &upload_file, michael@0: const string &file_part_name, michael@0: const string &proxy, michael@0: const string &proxy_user_pwd, michael@0: const string &ca_certificate_file, michael@0: string *response_body, michael@0: long *response_code, michael@0: string *error_description) { michael@0: if (response_code != NULL) michael@0: *response_code = 0; michael@0: michael@0: if (!CheckParameters(parameters)) michael@0: return false; michael@0: michael@0: void *curl_lib = dlopen("libcurl.so", RTLD_NOW); michael@0: if (!curl_lib) { michael@0: if (error_description != NULL) michael@0: *error_description = dlerror(); michael@0: curl_lib = dlopen("libcurl.so.4", RTLD_NOW); michael@0: } michael@0: if (!curl_lib) { michael@0: // Debian gives libcurl a different name when it is built against GnuTLS michael@0: // instead of OpenSSL. michael@0: curl_lib = dlopen("libcurl-gnutls.so.4", RTLD_NOW); michael@0: } michael@0: if (!curl_lib) { michael@0: curl_lib = dlopen("libcurl.so.3", RTLD_NOW); michael@0: } michael@0: if (!curl_lib) { michael@0: return false; michael@0: } michael@0: michael@0: CURL* (*curl_easy_init)(void); michael@0: *(void**) (&curl_easy_init) = dlsym(curl_lib, "curl_easy_init"); michael@0: CURL *curl = (*curl_easy_init)(); michael@0: if (error_description != NULL) michael@0: *error_description = "No Error"; michael@0: michael@0: if (!curl) { michael@0: dlclose(curl_lib); michael@0: return false; michael@0: } michael@0: michael@0: CURLcode err_code = CURLE_OK; michael@0: CURLcode (*curl_easy_setopt)(CURL *, CURLoption, ...); michael@0: *(void**) (&curl_easy_setopt) = dlsym(curl_lib, "curl_easy_setopt"); michael@0: (*curl_easy_setopt)(curl, CURLOPT_URL, url.c_str()); michael@0: (*curl_easy_setopt)(curl, CURLOPT_USERAGENT, kUserAgent); michael@0: // Set proxy information if necessary. michael@0: if (!proxy.empty()) michael@0: (*curl_easy_setopt)(curl, CURLOPT_PROXY, proxy.c_str()); michael@0: if (!proxy_user_pwd.empty()) michael@0: (*curl_easy_setopt)(curl, CURLOPT_PROXYUSERPWD, proxy_user_pwd.c_str()); michael@0: michael@0: if (!ca_certificate_file.empty()) michael@0: (*curl_easy_setopt)(curl, CURLOPT_CAINFO, ca_certificate_file.c_str()); michael@0: michael@0: struct curl_httppost *formpost = NULL; michael@0: struct curl_httppost *lastptr = NULL; michael@0: // Add form data. michael@0: CURLFORMcode (*curl_formadd)(struct curl_httppost **, struct curl_httppost **, ...); michael@0: *(void**) (&curl_formadd) = dlsym(curl_lib, "curl_formadd"); michael@0: map::const_iterator iter = parameters.begin(); michael@0: for (; iter != parameters.end(); ++iter) michael@0: (*curl_formadd)(&formpost, &lastptr, michael@0: CURLFORM_COPYNAME, iter->first.c_str(), michael@0: CURLFORM_COPYCONTENTS, iter->second.c_str(), michael@0: CURLFORM_END); michael@0: michael@0: // Add form file. michael@0: (*curl_formadd)(&formpost, &lastptr, michael@0: CURLFORM_COPYNAME, file_part_name.c_str(), michael@0: CURLFORM_FILE, upload_file.c_str(), michael@0: CURLFORM_END); michael@0: michael@0: (*curl_easy_setopt)(curl, CURLOPT_HTTPPOST, formpost); michael@0: michael@0: // Disable 100-continue header. michael@0: struct curl_slist *headerlist = NULL; michael@0: char buf[] = "Expect:"; michael@0: struct curl_slist* (*curl_slist_append)(struct curl_slist *, const char *); michael@0: *(void**) (&curl_slist_append) = dlsym(curl_lib, "curl_slist_append"); michael@0: headerlist = (*curl_slist_append)(headerlist, buf); michael@0: (*curl_easy_setopt)(curl, CURLOPT_HTTPHEADER, headerlist); michael@0: michael@0: if (response_body != NULL) { michael@0: (*curl_easy_setopt)(curl, CURLOPT_WRITEFUNCTION, WriteCallback); michael@0: (*curl_easy_setopt)(curl, CURLOPT_WRITEDATA, michael@0: reinterpret_cast(response_body)); michael@0: } michael@0: michael@0: // Fail if 400+ is returned from the web server. michael@0: (*curl_easy_setopt)(curl, CURLOPT_FAILONERROR, 1); michael@0: michael@0: CURLcode (*curl_easy_perform)(CURL *); michael@0: *(void**) (&curl_easy_perform) = dlsym(curl_lib, "curl_easy_perform"); michael@0: err_code = (*curl_easy_perform)(curl); michael@0: if (response_code != NULL) { michael@0: CURLcode (*curl_easy_getinfo)(CURL *, CURLINFO, ...); michael@0: *(void**) (&curl_easy_getinfo) = dlsym(curl_lib, "curl_easy_getinfo"); michael@0: (*curl_easy_getinfo)(curl, CURLINFO_RESPONSE_CODE, response_code); michael@0: } michael@0: const char* (*curl_easy_strerror)(CURLcode); michael@0: *(void**) (&curl_easy_strerror) = dlsym(curl_lib, "curl_easy_strerror"); michael@0: #ifndef NDEBUG michael@0: if (err_code != CURLE_OK) michael@0: fprintf(stderr, "Failed to send http request to %s, error: %s\n", michael@0: url.c_str(), michael@0: (*curl_easy_strerror)(err_code)); michael@0: #endif michael@0: if (error_description != NULL) michael@0: *error_description = (*curl_easy_strerror)(err_code); michael@0: michael@0: void (*curl_easy_cleanup)(CURL *); michael@0: *(void**) (&curl_easy_cleanup) = dlsym(curl_lib, "curl_easy_cleanup"); michael@0: (*curl_easy_cleanup)(curl); michael@0: if (formpost != NULL) { michael@0: void (*curl_formfree)(struct curl_httppost *); michael@0: *(void**) (&curl_formfree) = dlsym(curl_lib, "curl_formfree"); michael@0: (*curl_formfree)(formpost); michael@0: } michael@0: if (headerlist != NULL) { michael@0: void (*curl_slist_free_all)(struct curl_slist *); michael@0: *(void**) (&curl_slist_free_all) = dlsym(curl_lib, "curl_slist_free_all"); michael@0: (*curl_slist_free_all)(headerlist); michael@0: } michael@0: dlclose(curl_lib); michael@0: return err_code == CURLE_OK; michael@0: } michael@0: michael@0: // static michael@0: bool HTTPUpload::CheckParameters(const map ¶meters) { michael@0: for (map::const_iterator pos = parameters.begin(); michael@0: pos != parameters.end(); ++pos) { michael@0: const string &str = pos->first; michael@0: if (str.size() == 0) michael@0: return false; // disallow empty parameter names michael@0: for (unsigned int i = 0; i < str.size(); ++i) { michael@0: int c = str[i]; michael@0: if (c < 32 || c == '"' || c > 127) { michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: } // namespace google_breakpad