| // Copyright (c) 2009, Google Inc. | 
 | // All rights reserved. | 
 | // | 
 | // Redistribution and use in source and binary forms, with or without | 
 | // modification, are permitted provided that the following conditions are | 
 | // met: | 
 | // | 
 | //     * Redistributions of source code must retain the above copyright | 
 | // notice, this list of conditions and the following disclaimer. | 
 | //     * Redistributions in binary form must reproduce the above | 
 | // copyright notice, this list of conditions and the following disclaimer | 
 | // in the documentation and/or other materials provided with the | 
 | // distribution. | 
 | //     * Neither the name of Google Inc. nor the names of its | 
 | // contributors may be used to endorse or promote products derived from | 
 | // this software without specific prior written permission. | 
 | // | 
 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | 
 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | 
 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | 
 | // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | 
 | // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | 
 | // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | 
 | // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | 
 | // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | 
 | // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | 
 | // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | 
 | // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | 
 |  | 
 | #include <dlfcn.h> | 
 |  | 
 | #include <iostream> | 
 | #include <string> | 
 |  | 
 | #include "common/linux/libcurl_wrapper.h" | 
 | #include "common/using_std_string.h" | 
 |  | 
 | namespace google_breakpad { | 
 | LibcurlWrapper::LibcurlWrapper() | 
 |     : init_ok_(false), | 
 |       curl_lib_(nullptr), | 
 |       last_curl_error_(""), | 
 |       curl_(nullptr), | 
 |       formpost_(nullptr), | 
 |       lastptr_(nullptr), | 
 |       headerlist_(nullptr) {} | 
 |  | 
 | LibcurlWrapper::~LibcurlWrapper() { | 
 |   if (init_ok_) { | 
 |     (*easy_cleanup_)(curl_); | 
 |     dlclose(curl_lib_); | 
 |   } | 
 | } | 
 |  | 
 | bool LibcurlWrapper::SetProxy(const string& proxy_host, | 
 |                               const string& proxy_userpwd) { | 
 |   if (!CheckInit()) return false; | 
 |  | 
 |   // Set proxy information if necessary. | 
 |   if (!proxy_host.empty()) { | 
 |     (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str()); | 
 |   } else { | 
 |     std::cout << "SetProxy called with empty proxy host."; | 
 |     return false; | 
 |   } | 
 |   if (!proxy_userpwd.empty()) { | 
 |     (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str()); | 
 |   } else { | 
 |     std::cout << "SetProxy called with empty proxy username/password."; | 
 |     return false; | 
 |   } | 
 |   std::cout << "Set proxy host to " << proxy_host; | 
 |   return true; | 
 | } | 
 |  | 
 | bool LibcurlWrapper::AddFile(const string& upload_file_path, | 
 |                              const string& basename) { | 
 |   if (!CheckInit()) return false; | 
 |  | 
 |   std::cout << "Adding " << upload_file_path << " to form upload."; | 
 |   // Add form file. | 
 |   (*formadd_)(&formpost_, &lastptr_, | 
 |               CURLFORM_COPYNAME, basename.c_str(), | 
 |               CURLFORM_FILE, upload_file_path.c_str(), | 
 |               CURLFORM_END); | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | // Callback to get the response data from server. | 
 | static size_t WriteCallback(void* ptr, size_t size, | 
 |                             size_t nmemb, void* userp) { | 
 |   if (!userp) | 
 |     return 0; | 
 |  | 
 |   string* response = reinterpret_cast<string*>(userp); | 
 |   size_t real_size = size * nmemb; | 
 |   response->append(reinterpret_cast<char*>(ptr), real_size); | 
 |   return real_size; | 
 | } | 
 |  | 
 | bool LibcurlWrapper::SendRequest(const string& url, | 
 |                                  const std::map<string, string>& parameters, | 
 |                                  long* http_status_code, | 
 |                                  string* http_header_data, | 
 |                                  string* http_response_data) { | 
 |   if (!CheckInit()) return false; | 
 |  | 
 |   std::map<string, string>::const_iterator iter = parameters.begin(); | 
 |   for (; iter != parameters.end(); ++iter) | 
 |     (*formadd_)(&formpost_, &lastptr_, | 
 |                 CURLFORM_COPYNAME, iter->first.c_str(), | 
 |                 CURLFORM_COPYCONTENTS, iter->second.c_str(), | 
 |                 CURLFORM_END); | 
 |  | 
 |   (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_); | 
 |  | 
 |   return SendRequestInner(url, http_status_code, http_header_data, | 
 |                           http_response_data); | 
 | } | 
 |  | 
 | bool LibcurlWrapper::SendGetRequest(const string& url, | 
 |                                     long* http_status_code, | 
 |                                     string* http_header_data, | 
 |                                     string* http_response_data) { | 
 |   if (!CheckInit()) return false; | 
 |  | 
 |   (*easy_setopt_)(curl_, CURLOPT_HTTPGET, 1L); | 
 |  | 
 |   return SendRequestInner(url, http_status_code, http_header_data, | 
 |                           http_response_data); | 
 | } | 
 |  | 
 | bool LibcurlWrapper::SendPutRequest(const string& url, | 
 |                                     const string& path, | 
 |                                     long* http_status_code, | 
 |                                     string* http_header_data, | 
 |                                     string* http_response_data) { | 
 |   if (!CheckInit()) return false; | 
 |  | 
 |   FILE* file = fopen(path.c_str(), "rb"); | 
 |   (*easy_setopt_)(curl_, CURLOPT_UPLOAD, 1L); | 
 |   (*easy_setopt_)(curl_, CURLOPT_PUT, 1L); | 
 |   (*easy_setopt_)(curl_, CURLOPT_READDATA, file); | 
 |  | 
 |   bool success = SendRequestInner(url, http_status_code, http_header_data, | 
 |                                   http_response_data); | 
 |  | 
 |   fclose(file); | 
 |   return success; | 
 | } | 
 |  | 
 | bool LibcurlWrapper::SendSimplePostRequest(const string& url, | 
 |                                            const string& body, | 
 |                                            const string& content_type, | 
 |                                            long* http_status_code, | 
 |                                            string* http_header_data, | 
 |                                            string* http_response_data) { | 
 |   if (!CheckInit()) return false; | 
 |  | 
 |   (*easy_setopt_)(curl_, CURLOPT_POSTFIELDSIZE, body.size()); | 
 |   (*easy_setopt_)(curl_, CURLOPT_COPYPOSTFIELDS, body.c_str()); | 
 |  | 
 |   if (!content_type.empty()) { | 
 |     string content_type_header = "Content-Type: " + content_type; | 
 |     headerlist_ = (*slist_append_)( | 
 |         headerlist_, | 
 |         content_type_header.c_str()); | 
 |   } | 
 |  | 
 |   return SendRequestInner(url, http_status_code, http_header_data, | 
 |                           http_response_data); | 
 | } | 
 |  | 
 | bool LibcurlWrapper::Init() { | 
 |   // First check to see if libcurl was statically linked: | 
 |   curl_lib_ = dlopen(nullptr, RTLD_NOW); | 
 |   if (curl_lib_ && | 
 |       (!dlsym(curl_lib_, "curl_easy_init") || | 
 |       !dlsym(curl_lib_, "curl_easy_setopt"))) { | 
 |     // Not statically linked, try again below. | 
 |     dlerror();  // Clear dlerror before attempting to open libraries. | 
 |     dlclose(curl_lib_); | 
 |     curl_lib_ = nullptr; | 
 |   } | 
 |   if (!curl_lib_) { | 
 |     curl_lib_ = dlopen("libcurl.so", RTLD_NOW); | 
 |   } | 
 |   if (!curl_lib_) { | 
 |     curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW); | 
 |   } | 
 |   if (!curl_lib_) { | 
 |     curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW); | 
 |   } | 
 |   if (!curl_lib_) { | 
 |     std::cout << "Could not find libcurl via dlopen"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   if (!SetFunctionPointers()) { | 
 |     std::cout << "Could not find function pointers"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   curl_ = (*easy_init_)(); | 
 |  | 
 |   last_curl_error_ = "No Error"; | 
 |  | 
 |   if (!curl_) { | 
 |     dlclose(curl_lib_); | 
 |     std::cout << "Curl initialization failed"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   init_ok_ = true; | 
 |   return true; | 
 | } | 
 |  | 
 | #define SET_AND_CHECK_FUNCTION_POINTER(var, function_name, type) \ | 
 |   var = reinterpret_cast<type>(dlsym(curl_lib_, function_name)); \ | 
 |   if (!var) { \ | 
 |     std::cout << "Could not find libcurl function " << function_name; \ | 
 |     init_ok_ = false; \ | 
 |     return false; \ | 
 |   } | 
 |  | 
 | bool LibcurlWrapper::SetFunctionPointers() { | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(easy_init_, | 
 |                                  "curl_easy_init", | 
 |                                  CURL*(*)()); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_, | 
 |                                  "curl_easy_setopt", | 
 |                                  CURLcode(*)(CURL*, CURLoption, ...)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(formadd_, "curl_formadd", | 
 |       CURLFORMcode(*)(curl_httppost**, curl_httppost**, ...)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(slist_append_, "curl_slist_append", | 
 |       curl_slist*(*)(curl_slist*, const char*)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(easy_perform_, | 
 |                                  "curl_easy_perform", | 
 |                                  CURLcode(*)(CURL*)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_, | 
 |                                  "curl_easy_cleanup", | 
 |                                  void(*)(CURL*)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(easy_getinfo_, | 
 |                                  "curl_easy_getinfo", | 
 |                                  CURLcode(*)(CURL*, CURLINFO info, ...)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(easy_reset_, | 
 |                                  "curl_easy_reset", | 
 |                                  void(*)(CURL*)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_, | 
 |                                  "curl_slist_free_all", | 
 |                                  void(*)(curl_slist*)); | 
 |  | 
 |   SET_AND_CHECK_FUNCTION_POINTER(formfree_, | 
 |                                  "curl_formfree", | 
 |                                  void(*)(curl_httppost*)); | 
 |   return true; | 
 | } | 
 |  | 
 | bool LibcurlWrapper::SendRequestInner(const string& url, | 
 |                                       long* http_status_code, | 
 |                                       string* http_header_data, | 
 |                                       string* http_response_data) { | 
 |   string url_copy(url); | 
 |   (*easy_setopt_)(curl_, CURLOPT_URL, url_copy.c_str()); | 
 |  | 
 |   // Disable 100-continue header. | 
 |   char buf[] = "Expect:"; | 
 |   headerlist_ = (*slist_append_)(headerlist_, buf); | 
 |   (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_); | 
 |  | 
 |   if (http_response_data != nullptr) { | 
 |     http_response_data->clear(); | 
 |     (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback); | 
 |     (*easy_setopt_)(curl_, CURLOPT_WRITEDATA, | 
 |                     reinterpret_cast<void*>(http_response_data)); | 
 |   } | 
 |   if (http_header_data != nullptr) { | 
 |     http_header_data->clear(); | 
 |     (*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback); | 
 |     (*easy_setopt_)(curl_, CURLOPT_HEADERDATA, | 
 |                     reinterpret_cast<void*>(http_header_data)); | 
 |   } | 
 |   CURLcode err_code = CURLE_OK; | 
 |   err_code = (*easy_perform_)(curl_); | 
 |   easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)> | 
 |       (dlsym(curl_lib_, "curl_easy_strerror")); | 
 |  | 
 |   if (http_status_code != nullptr) { | 
 |     (*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code); | 
 |   } | 
 |  | 
 | #ifndef NDEBUG | 
 |   if (err_code != CURLE_OK) | 
 |     fprintf(stderr, "Failed to send http request to %s, error: %s\n", | 
 |             url.c_str(), | 
 |             (*easy_strerror_)(err_code)); | 
 | #endif | 
 |  | 
 |   Reset(); | 
 |  | 
 |   return err_code == CURLE_OK; | 
 | } | 
 |  | 
 | void LibcurlWrapper::Reset() { | 
 |   if (headerlist_ != nullptr) { | 
 |     (*slist_free_all_)(headerlist_); | 
 |     headerlist_ = nullptr; | 
 |   } | 
 |  | 
 |   if (formpost_ != nullptr) { | 
 |     (*formfree_)(formpost_); | 
 |     formpost_ = nullptr; | 
 |   } | 
 |  | 
 |   (*easy_reset_)(curl_); | 
 | } | 
 |  | 
 | bool LibcurlWrapper::CheckInit() { | 
 |   if (!init_ok_) { | 
 |     std::cout << "LibcurlWrapper: You must call Init(), and have it return " | 
 |                  "'true' before invoking any other methods.\n"; | 
 |     return false; | 
 |   } | 
 |  | 
 |   return true; | 
 | } | 
 |  | 
 | }  // namespace google_breakpad |