/ src / common / linux / libcurl_wrapper.cc
libcurl_wrapper.cc
  1  // Copyright 2009 Google LLC
  2  //
  3  // Redistribution and use in source and binary forms, with or without
  4  // modification, are permitted provided that the following conditions are
  5  // met:
  6  //
  7  //     * Redistributions of source code must retain the above copyright
  8  // notice, this list of conditions and the following disclaimer.
  9  //     * Redistributions in binary form must reproduce the above
 10  // copyright notice, this list of conditions and the following disclaimer
 11  // in the documentation and/or other materials provided with the
 12  // distribution.
 13  //     * Neither the name of Google LLC nor the names of its
 14  // contributors may be used to endorse or promote products derived from
 15  // this software without specific prior written permission.
 16  //
 17  // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 18  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 19  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 20  // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 21  // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 22  // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 23  // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 24  // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 25  // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 26  // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 27  // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 28  
 29  #ifdef HAVE_CONFIG_H
 30  #include <config.h>  // Must come first
 31  #endif
 32  
 33  #include <dlfcn.h>
 34  
 35  #include <iostream>
 36  #include <string>
 37  
 38  #include "common/linux/libcurl_wrapper.h"
 39  #include "common/using_std_string.h"
 40  
 41  namespace google_breakpad {
 42  LibcurlWrapper::LibcurlWrapper()
 43      : init_ok_(false),
 44        curl_lib_(nullptr),
 45        last_curl_error_(""),
 46        curl_(nullptr),
 47        formpost_(nullptr),
 48        lastptr_(nullptr),
 49        headerlist_(nullptr) {}
 50  
 51  LibcurlWrapper::~LibcurlWrapper() {
 52    if (init_ok_) {
 53      (*easy_cleanup_)(curl_);
 54      (*global_cleanup_)();
 55      dlclose(curl_lib_);
 56    }
 57  }
 58  
 59  bool LibcurlWrapper::SetProxy(const string& proxy_host,
 60                                const string& proxy_userpwd) {
 61    if (!CheckInit()) return false;
 62  
 63    // Set proxy information if necessary.
 64    if (!proxy_host.empty()) {
 65      (*easy_setopt_)(curl_, CURLOPT_PROXY, proxy_host.c_str());
 66    } else {
 67      std::cout << "SetProxy called with empty proxy host.";
 68      return false;
 69    }
 70    if (!proxy_userpwd.empty()) {
 71      (*easy_setopt_)(curl_, CURLOPT_PROXYUSERPWD, proxy_userpwd.c_str());
 72    } else {
 73      std::cout << "SetProxy called with empty proxy username/password.";
 74      return false;
 75    }
 76    std::cout << "Set proxy host to " << proxy_host;
 77    return true;
 78  }
 79  
 80  bool LibcurlWrapper::AddFile(const string& upload_file_path,
 81                               const string& basename) {
 82    if (!CheckInit()) return false;
 83  
 84    std::cout << "Adding " << upload_file_path << " to form upload.";
 85    // Add form file.
 86    (*formadd_)(&formpost_, &lastptr_,
 87                CURLFORM_COPYNAME, basename.c_str(),
 88                CURLFORM_FILE, upload_file_path.c_str(),
 89                CURLFORM_END);
 90  
 91    return true;
 92  }
 93  
 94  // Callback to get the response data from server.
 95  static size_t WriteCallback(void* ptr, size_t size,
 96                              size_t nmemb, void* userp) {
 97    if (!userp)
 98      return 0;
 99  
100    string* response = reinterpret_cast<string*>(userp);
101    size_t real_size = size * nmemb;
102    response->append(reinterpret_cast<char*>(ptr), real_size);
103    return real_size;
104  }
105  
106  bool LibcurlWrapper::SendRequest(const string& url,
107                                   const std::map<string, string>& parameters,
108                                   long* http_status_code,
109                                   string* http_header_data,
110                                   string* http_response_data) {
111    if (!CheckInit()) return false;
112  
113    std::map<string, string>::const_iterator iter = parameters.begin();
114    for (; iter != parameters.end(); ++iter)
115      (*formadd_)(&formpost_, &lastptr_,
116                  CURLFORM_COPYNAME, iter->first.c_str(),
117                  CURLFORM_COPYCONTENTS, iter->second.c_str(),
118                  CURLFORM_END);
119  
120    (*easy_setopt_)(curl_, CURLOPT_HTTPPOST, formpost_);
121  
122    return SendRequestInner(url, http_status_code, http_header_data,
123                            http_response_data);
124  }
125  
126  bool LibcurlWrapper::SendGetRequest(const string& url,
127                                      long* http_status_code,
128                                      string* http_header_data,
129                                      string* http_response_data) {
130    if (!CheckInit()) return false;
131  
132    (*easy_setopt_)(curl_, CURLOPT_HTTPGET, 1L);
133  
134    return SendRequestInner(url, http_status_code, http_header_data,
135                            http_response_data);
136  }
137  
138  bool LibcurlWrapper::SendPutRequest(const string& url,
139                                      const string& path,
140                                      long* http_status_code,
141                                      string* http_header_data,
142                                      string* http_response_data) {
143    if (!CheckInit()) return false;
144  
145    FILE* file = fopen(path.c_str(), "rb");
146    (*easy_setopt_)(curl_, CURLOPT_UPLOAD, 1L);
147    (*easy_setopt_)(curl_, CURLOPT_PUT, 1L);
148    (*easy_setopt_)(curl_, CURLOPT_READDATA, file);
149  
150    bool success = SendRequestInner(url, http_status_code, http_header_data,
151                                    http_response_data);
152  
153    fclose(file);
154    return success;
155  }
156  
157  bool LibcurlWrapper::SendSimplePostRequest(const string& url,
158                                             const string& body,
159                                             const string& content_type,
160                                             long* http_status_code,
161                                             string* http_header_data,
162                                             string* http_response_data) {
163    if (!CheckInit()) return false;
164  
165    (*easy_setopt_)(curl_, CURLOPT_POSTFIELDSIZE, body.size());
166    (*easy_setopt_)(curl_, CURLOPT_COPYPOSTFIELDS, body.c_str());
167  
168    if (!content_type.empty()) {
169      string content_type_header = "Content-Type: " + content_type;
170      headerlist_ = (*slist_append_)(
171          headerlist_,
172          content_type_header.c_str());
173    }
174  
175    return SendRequestInner(url, http_status_code, http_header_data,
176                            http_response_data);
177  }
178  
179  bool LibcurlWrapper::Init() {
180    // First check to see if libcurl was statically linked:
181    curl_lib_ = dlopen(nullptr, RTLD_NOW);
182    if (curl_lib_ &&
183        (!dlsym(curl_lib_, "curl_easy_init") ||
184        !dlsym(curl_lib_, "curl_easy_setopt"))) {
185      // Not statically linked, try again below.
186      dlerror();  // Clear dlerror before attempting to open libraries.
187      dlclose(curl_lib_);
188      curl_lib_ = nullptr;
189    }
190    if (!curl_lib_) {
191      curl_lib_ = dlopen("libcurl.so", RTLD_NOW);
192    }
193    if (!curl_lib_) {
194      curl_lib_ = dlopen("libcurl.so.4", RTLD_NOW);
195    }
196    if (!curl_lib_) {
197      curl_lib_ = dlopen("libcurl.so.3", RTLD_NOW);
198    }
199    if (!curl_lib_) {
200      std::cout << "Could not find libcurl via dlopen";
201      return false;
202    }
203  
204    if (!SetFunctionPointers()) {
205      std::cout << "Could not find function pointers";
206      return false;
207    }
208  
209    curl_ = (*easy_init_)();
210  
211    last_curl_error_ = "No Error";
212  
213    if (!curl_) {
214      dlclose(curl_lib_);
215      std::cout << "Curl initialization failed";
216      return false;
217    }
218  
219    init_ok_ = true;
220    return true;
221  }
222  
223  #define SET_AND_CHECK_FUNCTION_POINTER(var, function_name, type) \
224    var = reinterpret_cast<type>(dlsym(curl_lib_, function_name)); \
225    if (!var) { \
226      std::cout << "Could not find libcurl function " << function_name; \
227      init_ok_ = false; \
228      return false; \
229    }
230  
231  bool LibcurlWrapper::SetFunctionPointers() {
232  
233    SET_AND_CHECK_FUNCTION_POINTER(easy_init_,
234                                   "curl_easy_init",
235                                   CURL*(*)());
236  
237    SET_AND_CHECK_FUNCTION_POINTER(easy_setopt_,
238                                   "curl_easy_setopt",
239                                   CURLcode(*)(CURL*, CURLoption, ...));
240  
241    SET_AND_CHECK_FUNCTION_POINTER(formadd_, "curl_formadd",
242        CURLFORMcode(*)(curl_httppost**, curl_httppost**, ...));
243  
244    SET_AND_CHECK_FUNCTION_POINTER(slist_append_, "curl_slist_append",
245        curl_slist*(*)(curl_slist*, const char*));
246  
247    SET_AND_CHECK_FUNCTION_POINTER(easy_perform_,
248                                   "curl_easy_perform",
249                                   CURLcode(*)(CURL*));
250  
251    SET_AND_CHECK_FUNCTION_POINTER(easy_cleanup_,
252                                   "curl_easy_cleanup",
253                                   void(*)(CURL*));
254  
255    SET_AND_CHECK_FUNCTION_POINTER(easy_getinfo_,
256                                   "curl_easy_getinfo",
257                                   CURLcode(*)(CURL*, CURLINFO info, ...));
258  
259    SET_AND_CHECK_FUNCTION_POINTER(easy_reset_,
260                                   "curl_easy_reset",
261                                   void(*)(CURL*));
262  
263    SET_AND_CHECK_FUNCTION_POINTER(slist_free_all_,
264                                   "curl_slist_free_all",
265                                   void(*)(curl_slist*));
266  
267    SET_AND_CHECK_FUNCTION_POINTER(formfree_,
268                                   "curl_formfree",
269                                   void(*)(curl_httppost*));
270  
271    SET_AND_CHECK_FUNCTION_POINTER(global_cleanup_,
272                                   "curl_global_cleanup",
273                                   void(*)(void));
274    return true;
275  }
276  
277  bool LibcurlWrapper::SendRequestInner(const string& url,
278                                        long* http_status_code,
279                                        string* http_header_data,
280                                        string* http_response_data) {
281    string url_copy(url);
282    (*easy_setopt_)(curl_, CURLOPT_URL, url_copy.c_str());
283  
284    // Disable 100-continue header.
285    char buf[] = "Expect:";
286    headerlist_ = (*slist_append_)(headerlist_, buf);
287    (*easy_setopt_)(curl_, CURLOPT_HTTPHEADER, headerlist_);
288  
289    if (http_response_data != nullptr) {
290      http_response_data->clear();
291      (*easy_setopt_)(curl_, CURLOPT_WRITEFUNCTION, WriteCallback);
292      (*easy_setopt_)(curl_, CURLOPT_WRITEDATA,
293                      reinterpret_cast<void*>(http_response_data));
294    }
295    if (http_header_data != nullptr) {
296      http_header_data->clear();
297      (*easy_setopt_)(curl_, CURLOPT_HEADERFUNCTION, WriteCallback);
298      (*easy_setopt_)(curl_, CURLOPT_HEADERDATA,
299                      reinterpret_cast<void*>(http_header_data));
300    }
301    CURLcode err_code = CURLE_OK;
302    err_code = (*easy_perform_)(curl_);
303    easy_strerror_ = reinterpret_cast<const char* (*)(CURLcode)>
304        (dlsym(curl_lib_, "curl_easy_strerror"));
305  
306    if (http_status_code != nullptr) {
307      (*easy_getinfo_)(curl_, CURLINFO_RESPONSE_CODE, http_status_code);
308    }
309  
310    if (err_code != CURLE_OK)
311      fprintf(stderr, "Failed to send http request to %s, error: %s\n",
312              url.c_str(),
313              (*easy_strerror_)(err_code));
314  
315    Reset();
316  
317    return err_code == CURLE_OK;
318  }
319  
320  void LibcurlWrapper::Reset() {
321    if (headerlist_ != nullptr) {
322      (*slist_free_all_)(headerlist_);
323      headerlist_ = nullptr;
324    }
325  
326    if (formpost_ != nullptr) {
327      (*formfree_)(formpost_);
328      formpost_ = nullptr;
329    }
330  
331    (*easy_reset_)(curl_);
332  }
333  
334  bool LibcurlWrapper::CheckInit() {
335    if (!init_ok_) {
336      std::cout << "LibcurlWrapper: You must call Init(), and have it return "
337                   "'true' before invoking any other methods.\n";
338      return false;
339    }
340  
341    return true;
342  }
343  
344  }  // namespace google_breakpad