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