1 /**
2 *
3 * Ulfius Framework
4 *
5 * REST framework library
6 *
7 * u_send_request.c: send request related functions defintions
8 *
9 * Copyright 2015-2020 Nicolas Mora <mail@babelouest.org>
10 *
11 * This program is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public License
13 * as published by the Free Software Foundation;
14 * version 2.1 of the License.
15 *
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU GENERAL PUBLIC LICENSE for more details.
20 *
21 * You should have received a copy of the GNU General Public
22 * License along with this library. If not, see <http://www.gnu.org/licenses/>.
23 *
24 */
25
26 /**
27 * Enables POSIX functions on non-MSVC targets.
28 * This is required for gmtime_r() which is not part of the ISO C standard.
29 */
30 #if !defined(_MSC_VER) && (defined (__MINGW32__) || defined (__MINGW64__))
31 #define _POSIX_C_SOURCE 200112L
32 #endif
33
34 #include "u_private.h"
35 #include "ulfius.h"
36
37 #ifndef U_DISABLE_CURL
38
39 #include <stdlib.h>
40 #include <ctype.h>
41 #include <curl/curl.h>
42 #include <string.h>
43
44 #define U_ACCEPT_HEADER "Accept-Encoding"
45
46 #ifdef _MSC_VER
47 #define strtok_r strtok_s
48
gmtime_r(const time_t * t,struct tm * r)49 struct tm * gmtime_r(const time_t* t, struct tm* r) {
50 // gmtime is threadsafe in windows
51 struct tm* that = gmtime(t);
52 if (that != NULL) {
53 *r = *that;
54 return r;
55 } else {
56 return NULL;
57 }
58 }
59 #endif
60
61 /**
62 * Internal structure used to store temporarily the response body
63 */
64 struct _u_body {
65 char * data;
66 size_t size;
67 };
68
69 /**
70 * ulfius_send_smtp_email body fill function and structures
71 */
72 struct _u_smtp_payload {
73 size_t offset;
74 size_t len;
75 char * data;
76 };
77
78 /**
79 * ulfius_write_body
80 * Internal function used to write the body response into a _body structure
81 */
ulfius_write_body(void * contents,size_t size,size_t nmemb,void * user_data)82 static size_t ulfius_write_body(void * contents, size_t size, size_t nmemb, void * user_data) {
83 size_t realsize = size * nmemb;
84 struct _u_body * body_data = (struct _u_body *) user_data;
85
86 body_data->data = o_realloc(body_data->data, body_data->size + realsize + 1);
87 if(body_data->data == NULL) {
88 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for body_data->data");
89 return 0;
90 }
91
92 memcpy(&(body_data->data[body_data->size]), contents, realsize);
93 body_data->size += realsize;
94 body_data->data[body_data->size] = 0;
95
96 return realsize;
97 }
98
99 /**
100 * write_header
101 * Write the header value into the response map_header structure
102 * return the size_t of the header written
103 */
write_header(void * buffer,size_t size,size_t nitems,void * user_data)104 static size_t write_header(void * buffer, size_t size, size_t nitems, void * user_data) {
105
106 struct _u_response * response = (struct _u_response *) user_data;
107 char * header = (char *)buffer, * key, * value, * saveptr, * tmp;
108
109 if (o_strchr(header, ':') != NULL) {
110 if (response->map_header != NULL) {
111 // Expecting a header (key: value)
112 key = trimwhitespace(strtok_r(header, ":", &saveptr));
113 value = trimwhitespace(strtok_r(NULL, "", &saveptr));
114
115 if (!u_map_has_key_case(response->map_header, key)) {
116 u_map_put(response->map_header, key, value);
117 } else {
118 tmp = msprintf("%s, %s", u_map_get_case(response->map_header, key), value);
119 if (u_map_remove_from_key_case(response->map_header, key) != U_OK || u_map_put(response->map_header, key, tmp)) {
120 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting header value for name %s", key);
121 }
122 o_free(tmp);
123 }
124 }
125 } else if (o_strlen(trimwhitespace(header)) > 0) {
126 // Expecting the HTTP/x.x header
127 if (response->protocol != NULL) {
128 o_free(response->protocol);
129 }
130 response->protocol = o_strdup(header);
131 if (response->protocol == NULL) {
132 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->protocol");
133 return 0;
134 }
135 }
136
137 return nitems * size;
138 }
139
smtp_payload_source(void * ptr,size_t size,size_t nmemb,void * userp)140 static size_t smtp_payload_source(void * ptr, size_t size, size_t nmemb, void * userp) {
141 struct _u_smtp_payload *upload_ctx = (struct _u_smtp_payload *)userp;
142 size_t len;
143
144 if ((size * nmemb) < (upload_ctx->len - upload_ctx->offset)) {
145 len = size*nmemb;
146 } else {
147 len = upload_ctx->len - upload_ctx->offset;
148 }
149 memcpy(ptr, upload_ctx->data+upload_ctx->offset, len);
150 upload_ctx->offset += len;
151 return len;
152 }
153
154 /**
155 * ulfius_send_http_request
156 * Send a HTTP request and store the result into a _u_response
157 * return U_OK on success
158 */
ulfius_send_http_request(const struct _u_request * request,struct _u_response * response)159 int ulfius_send_http_request(const struct _u_request * request, struct _u_response * response) {
160 struct _u_body body_data;
161 body_data.size = 0;
162 body_data.data = NULL;
163 int res;
164
165 res = ulfius_send_http_streaming_request(request, response, ulfius_write_body, (void *)&body_data);
166 if (res == U_OK && response != NULL) {
167 if (body_data.data != NULL && body_data.size > 0) {
168 response->binary_body = o_malloc(body_data.size);
169 if (response->binary_body == NULL) {
170 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for response->binary_body");
171 o_free(body_data.data);
172 return U_ERROR_MEMORY;
173 }
174 memcpy(response->binary_body, body_data.data, body_data.size);
175 response->binary_body_length = body_data.size;
176 }
177 o_free(body_data.data);
178 return U_OK;
179 } else {
180 o_free(body_data.data);
181 return res;
182 }
183 }
184
185 /**
186 * ulfius_send_http_streaming_request
187 * Send a HTTP request and store the result into a _u_response
188 * Except for the body which will be available using write_body_function in the write_body_data
189 * return U_OK on success
190 */
ulfius_send_http_streaming_request(const struct _u_request * request,struct _u_response * response,size_t (* write_body_function)(void * contents,size_t size,size_t nmemb,void * user_data),void * write_body_data)191 int ulfius_send_http_streaming_request(const struct _u_request * request,
192 struct _u_response * response,
193 size_t (* write_body_function)(void * contents, size_t size, size_t nmemb, void * user_data),
194 void * write_body_data) {
195 CURLcode res;
196 CURL * curl_handle = NULL;
197 struct curl_slist * header_list = NULL, * cookies_list = NULL;
198 char * key_esc = NULL, * value_esc = NULL, * cookie = NULL, * header = NULL, * fp = "?", * np = "&";
199 const char * value = NULL, ** keys = NULL;
200 int i, has_params = 0, ret, exit_loop;
201 struct _u_request * copy_request = NULL;
202
203 if (request != NULL) {
204 // Duplicate the request and work on it
205 if ((copy_request = ulfius_duplicate_request(request)) != NULL) {
206
207 if ((curl_handle = curl_easy_init()) != NULL) {
208 ret = U_OK;
209
210 // Here comes the fake loop with breaks to exit smoothly
211 do {
212 // Set proxy if defined
213 if (u_map_has_key_case(copy_request->map_header, U_ACCEPT_HEADER)) {
214 if (curl_easy_setopt(curl_handle, CURLOPT_ACCEPT_ENCODING, u_map_get_case(copy_request->map_header, U_ACCEPT_HEADER)) != CURLE_OK) {
215 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting accept encoding option");
216 ret = U_ERROR_LIBCURL;
217 break;
218 }
219 }
220
221 // Set basic auth if defined
222 if (copy_request->auth_basic_user != NULL && copy_request->auth_basic_password != NULL) {
223 if (curl_easy_setopt(curl_handle, CURLOPT_HTTPAUTH, CURLAUTH_BASIC) == CURLE_OK) {
224 if (curl_easy_setopt(curl_handle, CURLOPT_USERNAME, copy_request->auth_basic_user) != CURLE_OK ||
225 curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, copy_request->auth_basic_password) != CURLE_OK) {
226 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting HTTP Basic user name or password");
227 ret = U_ERROR_LIBCURL;
228 break;
229 }
230 } else {
231 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting HTTP Basic Auth option");
232 ret = U_ERROR_LIBCURL;
233 break;
234 }
235 }
236
237 #ifndef U_DISABLE_GNUTLS
238 // Set client certificate authentication if defined
239 if (request->client_cert_file != NULL && request->client_key_file != NULL) {
240 if (curl_easy_setopt(curl_handle, CURLOPT_SSLCERT, request->client_cert_file) != CURLE_OK) {
241 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting client certificate file");
242 ret = U_ERROR_LIBCURL;
243 break;
244 } else if (curl_easy_setopt(curl_handle, CURLOPT_SSLKEY, request->client_key_file) != CURLE_OK) {
245 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting client key file");
246 ret = U_ERROR_LIBCURL;
247 break;
248 } else if (request->client_key_password != NULL && curl_easy_setopt(curl_handle, CURLOPT_KEYPASSWD, request->client_key_password) != CURLE_OK) {
249 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting client key password");
250 ret = U_ERROR_LIBCURL;
251 break;
252 }
253 }
254 #endif
255
256 if (curl_easy_setopt(curl_handle, CURLOPT_NOPROGRESS, 1L) != CURLE_OK) {
257 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting CURLOPT_NOPROGRESS option");
258 ret = U_ERROR_LIBCURL;
259 break;
260 }
261
262 // Set proxy if defined
263 if (copy_request->proxy != NULL) {
264 if (curl_easy_setopt(curl_handle, CURLOPT_PROXY, copy_request->proxy) != CURLE_OK) {
265 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting proxy option");
266 ret = U_ERROR_LIBCURL;
267 break;
268 }
269 }
270
271 // follow redirection if set
272 if (copy_request->follow_redirect) {
273 if (curl_easy_setopt(curl_handle, CURLOPT_FOLLOWLOCATION, 1) != CURLE_OK) {
274 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting follow redirection option");
275 ret = U_ERROR_LIBCURL;
276 break;
277 }
278 }
279
280 #if MHD_VERSION >= 0x00095208
281 // Set network type
282 if (copy_request->network_type & U_USE_ALL) {
283 if (curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_WHATEVER) != CURLE_OK) {
284 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting IPRESOLVE WHATEVER option");
285 ret = U_ERROR_LIBCURL;
286 break;
287 }
288 } else if (copy_request->network_type & U_USE_IPV6) {
289 if (curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V6) != CURLE_OK) {
290 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting IPRESOLVE V6 option");
291 ret = U_ERROR_LIBCURL;
292 break;
293 }
294 } else {
295 if (curl_easy_setopt(curl_handle, CURLOPT_IPRESOLVE, CURL_IPRESOLVE_V4) != CURLE_OK) {
296 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting IPRESOLVE V4 option");
297 ret = U_ERROR_LIBCURL;
298 break;
299 }
300 }
301 #endif
302
303 has_params = (o_strchr(copy_request->http_url, '?') != NULL);
304 if (u_map_count(copy_request->map_url) > 0) {
305 // Append url parameters
306 keys = u_map_enum_keys(copy_request->map_url);
307
308 exit_loop = 0;
309 // Append parameters from map_url
310 for (i=0; !exit_loop && keys != NULL && keys[i] != NULL; i++) {
311 key_esc = curl_easy_escape(curl_handle, keys[i], 0);
312 if (key_esc != NULL) {
313 value = u_map_get(copy_request->map_url, keys[i]);
314 if (value != NULL) {
315 value_esc = curl_easy_escape(curl_handle, value, 0);
316 if (value_esc != NULL) {
317 if (!has_params) {
318 copy_request->http_url = mstrcatf(copy_request->http_url, "%s%s=%s", fp, key_esc, value_esc);
319 has_params = 1;
320 } else {
321 copy_request->http_url = mstrcatf(copy_request->http_url, "%s%s=%s", np, key_esc, value_esc);
322 }
323 curl_free(value_esc);
324 } else {
325 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_escape for url parameter value %s=%s", keys[i], value);
326 exit_loop = 1;
327 }
328 } else {
329 if (!has_params) {
330 copy_request->http_url = mstrcatf(copy_request->http_url, "%s%s", fp, key_esc);
331 has_params = 1;
332 } else {
333 copy_request->http_url = mstrcatf(copy_request->http_url, "%s%s", np, key_esc);
334 }
335 }
336 curl_free(key_esc);
337 } else {
338 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_escape for url key %s", keys[i]);
339 exit_loop = 1;
340 }
341 }
342 if (exit_loop) {
343 ret = U_ERROR_LIBCURL;
344 break;
345 }
346 }
347
348 if (u_map_count(copy_request->map_post_body) > 0) {
349 o_free(copy_request->binary_body);
350 copy_request->binary_body = NULL;
351 copy_request->binary_body_length = 0;
352 // Append MHD_HTTP_POST_ENCODING_FORM_URLENCODED post parameters
353 keys = u_map_enum_keys(copy_request->map_post_body);
354 exit_loop = 0;
355 for (i=0; !exit_loop && keys != NULL && keys[i] != NULL; i++) {
356 // Build parameter
357 key_esc = curl_easy_escape(curl_handle, keys[i], 0);
358 if (key_esc != NULL) {
359 value = u_map_get(copy_request->map_post_body, keys[i]);
360 if (value != NULL) {
361 value_esc = curl_easy_escape(curl_handle, value, 0);
362 if (value_esc != NULL) {
363 if (!i) {
364 copy_request->binary_body = mstrcatf(copy_request->binary_body, "%s=%s", key_esc, value_esc);
365 } else {
366 copy_request->binary_body = mstrcatf(copy_request->binary_body, "%s%s=%s", np, key_esc, value_esc);
367 }
368 copy_request->binary_body_length = o_strlen(copy_request->binary_body);
369 } else {
370 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_escape for body parameter value %s=%s", keys[i], value);
371 exit_loop = 1;
372 }
373 o_free(value_esc);
374 } else {
375 if (!i) {
376 copy_request->binary_body = mstrcatf(copy_request->binary_body, "%s", key_esc);
377 } else {
378 copy_request->binary_body = mstrcatf(copy_request->binary_body, "%s%s", np, key_esc);
379 }
380 copy_request->binary_body_length = o_strlen(copy_request->binary_body);
381 }
382 } else {
383 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_escape for body key %s", keys[i]);
384 exit_loop = 1;
385 }
386 o_free(key_esc);
387 }
388
389 if (exit_loop) {
390 ret = U_ERROR_LIBCURL;
391 break;
392 }
393
394 if (u_map_put(copy_request->map_header, ULFIUS_HTTP_HEADER_CONTENT, MHD_HTTP_POST_ENCODING_FORM_URLENCODED) != U_OK) {
395 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headr fields");
396 ret = U_ERROR_LIBCURL;
397 break;
398 }
399 }
400
401 // Set body content
402 if (copy_request->binary_body_length && copy_request->binary_body != NULL) {
403 if (copy_request->binary_body_length < 2147483648) {
404 if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE, (curl_off_t)copy_request->binary_body_length) != CURLE_OK) {
405 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields size");
406 ret = U_ERROR_LIBCURL;
407 break;
408 }
409 } else {
410 if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDSIZE_LARGE, (curl_off_t)copy_request->binary_body_length) != CURLE_OK) {
411 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields size large");
412 ret = U_ERROR_LIBCURL;
413 break;
414 }
415 }
416
417 if (curl_easy_setopt(curl_handle, CURLOPT_POSTFIELDS, copy_request->binary_body) != CURLE_OK) {
418 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting POST fields");
419 ret = U_ERROR_LIBCURL;
420 break;
421 }
422 }
423
424 if (u_map_count(copy_request->map_header) > 0) {
425 // Append map headers
426 keys = u_map_enum_keys(copy_request->map_header);
427 exit_loop = 0;
428 for (i=0; !exit_loop && keys != NULL && keys[i] != NULL; i++) {
429 // Build parameter
430 value = u_map_get(copy_request->map_header, keys[i]);
431 if (value != NULL) {
432 header = msprintf("%s:%s", keys[i], value);
433 if ((header_list = curl_slist_append(header_list, header)) == NULL) {
434 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_slist_append for header_list (1)");
435 exit_loop = 1;
436 }
437 o_free(header);
438 } else {
439 header = msprintf("%s:", keys[i]);
440 if ((header_list = curl_slist_append(header_list, header)) == NULL) {
441 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_slist_append for header_list (2)");
442 exit_loop = 1;
443 }
444 o_free(header);
445 }
446 }
447 if (exit_loop) {
448 ret = U_ERROR_LIBCURL;
449 break;
450 }
451 }
452
453 if (copy_request->map_cookie != NULL && u_map_count(copy_request->map_cookie) > 0) {
454 // Append cookies
455 keys = u_map_enum_keys(copy_request->map_cookie);
456 exit_loop = 0;
457 for (i=0; !exit_loop && keys != NULL && keys[i] != NULL; i++) {
458 // Build parameter
459 value = u_map_get(copy_request->map_cookie, keys[i]);
460 if (value != NULL) {
461 cookie = msprintf("%s=%s", keys[i], value);
462 if (curl_easy_setopt(curl_handle, CURLOPT_COOKIE, cookie) != CURLE_OK) {
463 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting cookie %s", cookie);
464 exit_loop = 1;
465 }
466 o_free(cookie);
467 } else {
468 cookie = msprintf("%s:", keys[i]);
469 if (curl_easy_setopt(curl_handle, CURLOPT_COOKIE, cookie) != CURLE_OK) {
470 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting cookie %s", cookie);
471 exit_loop = 1;
472 }
473 o_free(cookie);
474 }
475 }
476 if (exit_loop) {
477 ret = U_ERROR_LIBCURL;
478 break;
479 }
480 }
481
482 // Request parameters
483 if (curl_easy_setopt(curl_handle, CURLOPT_URL, copy_request->http_url) != CURLE_OK ||
484 curl_easy_setopt(curl_handle, CURLOPT_CUSTOMREQUEST, copy_request->http_verb!=NULL?copy_request->http_verb:"GET") != CURLE_OK ||
485 curl_easy_setopt(curl_handle, CURLOPT_HTTPHEADER, header_list) != CURLE_OK) {
486 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (1)");
487 ret = U_ERROR_LIBCURL;
488 break;
489 }
490
491 // Set CURLOPT_WRITEFUNCTION if specified
492 if (write_body_function != NULL && curl_easy_setopt(curl_handle, CURLOPT_WRITEFUNCTION, write_body_function) != CURLE_OK) {
493 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (2)");
494 ret = U_ERROR_LIBCURL;
495 break;
496 }
497
498 // Set CURLOPT_WRITEDATA if specified
499 if (write_body_data != NULL && curl_easy_setopt(curl_handle, CURLOPT_WRITEDATA, write_body_data) != CURLE_OK) {
500 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (3)");
501 ret = U_ERROR_LIBCURL;
502 break;
503 }
504
505 // Disable server certificate validation if needed
506 if (!copy_request->check_server_certificate) {
507 if (curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0) != CURLE_OK) {
508 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (4)");
509 ret = U_ERROR_LIBCURL;
510 break;
511 }
512 } else {
513 if (!(copy_request->check_server_certificate_flag & U_SSL_VERIFY_PEER)) {
514 if (curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0) != CURLE_OK) {
515 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (5)");
516 ret = U_ERROR_LIBCURL;
517 break;
518 }
519 }
520 if (!(copy_request->check_server_certificate_flag & U_SSL_VERIFY_HOSTNAME)) {
521 if (curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0) != CURLE_OK) {
522 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (6)");
523 ret = U_ERROR_LIBCURL;
524 break;
525 }
526 }
527 }
528
529 #if LIBCURL_VERSION_NUM >= 0x073400
530 // Disable proxy certificate validation if needed
531 if (!copy_request->check_proxy_certificate) {
532 if (curl_easy_setopt(curl_handle, CURLOPT_PROXY_SSL_VERIFYPEER, 0) != CURLE_OK || curl_easy_setopt(curl_handle, CURLOPT_PROXY_SSL_VERIFYHOST, 0) != CURLE_OK) {
533 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (7)");
534 ret = U_ERROR_LIBCURL;
535 break;
536 }
537 } else {
538 if (!(copy_request->check_proxy_certificate_flag & U_SSL_VERIFY_PEER)) {
539 if (curl_easy_setopt(curl_handle, CURLOPT_PROXY_SSL_VERIFYPEER, 0) != CURLE_OK) {
540 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (8)");
541 ret = U_ERROR_LIBCURL;
542 break;
543 }
544 }
545 if (!(copy_request->check_proxy_certificate_flag & U_SSL_VERIFY_HOSTNAME)) {
546 if (curl_easy_setopt(curl_handle, CURLOPT_PROXY_SSL_VERIFYHOST, 0) != CURLE_OK) {
547 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (9)");
548 ret = U_ERROR_LIBCURL;
549 break;
550 }
551 }
552 }
553 #endif
554
555 // Set request ca_path value
556 if (copy_request->ca_path) {
557 if (curl_easy_setopt(curl_handle, CURLOPT_CAPATH, copy_request->ca_path) != CURLE_OK) {
558 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (10)");
559 ret = U_ERROR_LIBCURL;
560 break;
561 }
562 }
563
564 // Set request timeout value
565 if (copy_request->timeout) {
566 if (curl_easy_setopt(curl_handle, CURLOPT_TIMEOUT, copy_request->timeout) != CURLE_OK) {
567 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl options (10)");
568 ret = U_ERROR_LIBCURL;
569 break;
570 }
571 }
572
573 // Response parameters
574 if (response != NULL) {
575 if (response->map_header != NULL) {
576 if (curl_easy_setopt(curl_handle, CURLOPT_HEADERFUNCTION, write_header) != CURLE_OK ||
577 curl_easy_setopt(curl_handle, CURLOPT_WRITEHEADER, response) != CURLE_OK) {
578 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting headers");
579 ret = U_ERROR_LIBCURL;
580 break;
581 }
582 }
583 }
584
585 if (curl_easy_setopt(curl_handle, CURLOPT_NOSIGNAL, 1) != CURLE_OK) {
586 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl CURLOPT_NOSIGNAL");
587 ret = U_ERROR_LIBCURL;
588 break;
589 }
590
591 if (curl_easy_setopt(curl_handle, CURLOPT_COOKIEFILE, "") != CURLE_OK) { // Apparently you have to do that to tell libcurl you'll need cookies afterwards
592 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error setting libcurl CURLOPT_COOKIEFILE");
593 ret = U_ERROR_LIBCURL;
594 break;
595 }
596
597 res = curl_easy_perform(curl_handle);
598 if (res != CURLE_OK) {
599 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_perform");
600 y_log_message(Y_LOG_LEVEL_DEBUG, "Ulfius - libcurl error: %d, error message '%s'", res, curl_easy_strerror(res));
601 ret = U_ERROR_LIBCURL;
602 break;
603 } else if (res == CURLE_OK && response != NULL) {
604 if (curl_easy_getinfo (curl_handle, CURLINFO_RESPONSE_CODE, &response->status) != CURLE_OK) {
605 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing http request, libcurl error: %d, error message '%s'", res, curl_easy_strerror(res));
606 ret = U_ERROR_LIBCURL;
607 break;
608 }
609
610 if (curl_easy_getinfo(curl_handle, CURLINFO_COOKIELIST, &cookies_list) == CURLE_OK) {
611 struct curl_slist * nc = cookies_list;
612 char * key = NULL, * value = NULL, * expires = NULL, * domain = NULL, * path = NULL;
613 int secure = 0, http_only = 0;
614
615 while (nc != NULL) {
616 char * nc_dup = o_strdup(nc->data), * saveptr, * elt;
617 int counter = 0;
618
619 if (nc_dup != NULL) {
620 elt = strtok_r(nc_dup, "\t", &saveptr);
621 while (elt != NULL) {
622 // libcurl cookie format is domain\tsecure\tpath\thttp_only\texpires\tkey\tvalue
623 switch (counter) {
624 case 0:
625 domain = o_strdup(elt);
626 break;
627 case 1:
628 secure = (0==o_strcmp(elt, "TRUE"));
629 break;
630 case 2:
631 path = o_strdup(elt);
632 break;
633 case 3:
634 http_only = (0==o_strcmp(elt, "TRUE"));
635 break;
636 case 4:
637 expires = o_strdup(elt);
638 break;
639 case 5:
640 key = o_strdup(elt);
641 break;
642 case 6:
643 value = o_strdup(elt);
644 break;
645 }
646 elt = strtok_r(NULL, "\t", &saveptr);
647 counter++;
648 }
649 if (ulfius_add_cookie_to_response(response, key, value, expires, 0, domain, path, secure, http_only) != U_OK) {
650 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error adding cookie %s/%s to response", key, value);
651 }
652 o_free(key);
653 o_free(value);
654 o_free(domain);
655 o_free(path);
656 o_free(expires);
657 }
658 o_free(nc_dup);
659 nc = nc->next;
660 }
661 } else {
662 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing http request, libcurl error: %d, error message %s", res, curl_easy_strerror(res));
663 ret = U_ERROR_LIBCURL;
664 }
665 curl_slist_free_all(cookies_list);
666 }
667 } while (0);
668 } else {
669 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_init");
670 ret = U_ERROR_LIBCURL;
671 }
672 ulfius_clean_request_full(copy_request);
673 curl_easy_cleanup(curl_handle);
674 curl_slist_free_all(header_list);
675 } else {
676 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error ulfius_duplicate_request");
677 ret = U_ERROR_MEMORY;
678 }
679 } else {
680 ret = U_ERROR_PARAMS;
681 }
682 return ret;
683 }
684
685 /**
686 * Send an email using libcurl
687 * email has the content-type specified in parameter
688 * host: smtp server host name
689 * port: tcp port number (optional, 0 for default)
690 * use_tls: true if the connection is tls secured
691 * verify_certificate: true if you want to disable the certificate verification on a tls server
692 * user: connection user name (optional, NULL: no user name)
693 * password: connection password (optional, NULL: no password)
694 * from: from address (mandatory)
695 * to: to recipient address (mandatory)
696 * cc: cc recipient address (optional, NULL: no cc)
697 * bcc: bcc recipient address (optional, NULL: no bcc)
698 * content_type: content-type to add to the e-mail body
699 * subject: email subject (mandatory)
700 * mail_body: email body (mandatory)
701 * return U_OK on success
702 */
ulfius_send_smtp_rich_email(const char * host,const int port,const int use_tls,const int verify_certificate,const char * user,const char * password,const char * from,const char * to,const char * cc,const char * bcc,const char * content_type,const char * subject,const char * mail_body)703 int ulfius_send_smtp_rich_email(const char * host,
704 const int port,
705 const int use_tls,
706 const int verify_certificate,
707 const char * user,
708 const char * password,
709 const char * from,
710 const char * to,
711 const char * cc,
712 const char * bcc,
713 const char * content_type,
714 const char * subject,
715 const char * mail_body) {
716 CURL * curl_handle;
717 CURLcode res = CURLE_OK;
718 char * smtp_url = NULL;
719 int cur_port, ret;
720 struct curl_slist * recipients = NULL;
721 struct _u_smtp_payload upload_ctx;
722 time_t now_sec;
723 struct tm now;
724 char date_str[129], * cc_str = NULL;
725
726 if (host != NULL && from != NULL && to != NULL && mail_body != NULL) {
727
728 curl_handle = curl_easy_init();
729 if (curl_handle != NULL) {
730 ret = U_OK;
731 upload_ctx.data = NULL;
732 upload_ctx.offset = 0;
733 upload_ctx.len = 0;
734 do {
735 if (port == 0 && !use_tls) {
736 cur_port = 25;
737 } else if (port == 0 && use_tls) {
738 cur_port = 587;
739 } else {
740 cur_port = port;
741 }
742 smtp_url = msprintf("smtp%s://%s:%d", use_tls?"s":"", host, cur_port);
743 if (smtp_url == NULL) {
744 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating memory for smtp_url");
745 ret = U_ERROR_MEMORY;
746 break;
747 }
748
749 if (curl_easy_setopt(curl_handle, CURLOPT_URL, smtp_url) != CURLE_OK) {
750 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for smtp_url");
751 ret = U_ERROR_LIBCURL;
752 break;
753 }
754
755 if (use_tls) {
756 if (curl_easy_setopt(curl_handle, CURLOPT_USE_SSL, (long)CURLUSESSL_ALL) != CURLE_OK) {
757 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_USE_SSL");
758 ret = U_ERROR_LIBCURL;
759 break;
760 }
761 }
762
763 if (use_tls && !verify_certificate) {
764 if (curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYPEER, 0L) != CURLE_OK) {
765 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_SSL_VERIFYPEER");
766 ret = U_ERROR_LIBCURL;
767 break;
768 }
769 if (curl_easy_setopt(curl_handle, CURLOPT_SSL_VERIFYHOST, 0L) != CURLE_OK) {
770 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_SSL_VERIFYHOST");
771 ret = U_ERROR_LIBCURL;
772 break;
773 }
774 }
775
776 if (user != NULL && password != NULL) {
777 if (curl_easy_setopt(curl_handle, CURLOPT_USERNAME, user) != CURLE_OK) {
778 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_USERNAME");
779 ret = U_ERROR_LIBCURL;
780 break;
781 }
782 if (curl_easy_setopt(curl_handle, CURLOPT_PASSWORD, password) != CURLE_OK) {
783 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_PASSWORD");
784 ret = U_ERROR_LIBCURL;
785 break;
786 }
787 }
788
789 if (curl_easy_setopt(curl_handle, CURLOPT_MAIL_FROM, from) != CURLE_OK) {
790 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_MAIL_FROM");
791 ret = U_ERROR_LIBCURL;
792 break;
793 }
794
795 if ((recipients = curl_slist_append(recipients, to)) == NULL) {
796 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_slist_append for recipients to");
797 ret = U_ERROR_LIBCURL;
798 break;
799 }
800 if (cc != NULL) {
801 if ((recipients = curl_slist_append(recipients, cc)) == NULL) {
802 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_slist_append for recipients cc");
803 ret = U_ERROR_LIBCURL;
804 break;
805 }
806 }
807 if (bcc != NULL) {
808 if ((recipients = curl_slist_append(recipients, bcc)) == NULL) {
809 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_slist_append for recipients bcc");
810 ret = U_ERROR_LIBCURL;
811 break;
812 }
813 }
814 if (curl_easy_setopt(curl_handle, CURLOPT_MAIL_RCPT, recipients) != CURLE_OK) {
815 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_MAIL_RCPT");
816 ret = U_ERROR_LIBCURL;
817 break;
818 }
819
820 time(&now_sec);
821 gmtime_r(&now_sec, &now);
822 #ifdef _WIN32
823 strftime(date_str, 128, "Date: %a, %d %b %Y %H:%M:%S %z", &now);
824 #else
825 strftime(date_str, 128, "Date: %a, %d %b %Y %T %z", &now);
826 #endif
827 if (cc != NULL) {
828 cc_str = msprintf("Cc: %s\r\n", cc);
829 } else {
830 cc_str = o_strdup("");
831 }
832 upload_ctx.data = msprintf("%s\r\n" // date_str
833 "To: %s\r\n"
834 "From: %s\r\n"
835 "%s"
836 "Subject: %s\r\n"
837 "Content-Type: %s\r\n\r\n%s\r\n",
838 date_str,
839 to,
840 from,
841 cc_str,
842 subject!=NULL?subject:"",
843 content_type,
844 mail_body);
845 if (upload_ctx.data == NULL) {
846 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error allocating resource for upload_ctx.data");
847 ret = U_ERROR_MEMORY;
848 break;
849 }
850 upload_ctx.len = o_strlen(upload_ctx.data);
851 if (curl_easy_setopt(curl_handle, CURLOPT_READFUNCTION, smtp_payload_source) != CURLE_OK) {
852 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_READFUNCTION");
853 ret = U_ERROR_LIBCURL;
854 break;
855 }
856 if (curl_easy_setopt(curl_handle, CURLOPT_READDATA, &upload_ctx) != CURLE_OK) {
857 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_READDATA");
858 ret = U_ERROR_LIBCURL;
859 break;
860 }
861 if (curl_easy_setopt(curl_handle, CURLOPT_UPLOAD, 1L) != CURLE_OK) {
862 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error curl_easy_setopt for CURLOPT_UPLOAD");
863 ret = U_ERROR_LIBCURL;
864 break;
865 }
866
867 if ((res = curl_easy_perform(curl_handle)) != CURLE_OK) {
868 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error sending smtp message, error message '%s'", curl_easy_strerror(res));
869 ret = U_ERROR_LIBCURL;
870 break;
871 }
872 } while (0);
873
874 curl_slist_free_all(recipients);
875 curl_easy_cleanup(curl_handle);
876 o_free(smtp_url);
877 o_free(cc_str);
878 o_free(upload_ctx.data);
879 } else {
880 y_log_message(Y_LOG_LEVEL_ERROR, "Ulfius - Error executing curl_easy_init");
881 ret = U_ERROR_LIBCURL;
882 }
883 } else {
884 ret = U_ERROR_PARAMS;
885 }
886 return ret;
887 }
888
889 /**
890 * Send an email using libcurl
891 * email is plain/text and UTF8 charset
892 * host: smtp server host name
893 * port: tcp port number (optional, 0 for default)
894 * use_tls: true if the connection is tls secured
895 * verify_certificate: true if you want to disable the certificate verification on a tls server
896 * user: connection user name (optional, NULL: no user name)
897 * password: connection password (optional, NULL: no password)
898 * from: from address (mandatory)
899 * to: to recipient address (mandatory)
900 * cc: cc recipient address (optional, NULL: no cc)
901 * bcc: bcc recipient address (optional, NULL: no bcc)
902 * subject: email subject (mandatory)
903 * mail_body: email body (mandatory)
904 * return U_OK on success
905 */
ulfius_send_smtp_email(const char * host,const int port,const int use_tls,const int verify_certificate,const char * user,const char * password,const char * from,const char * to,const char * cc,const char * bcc,const char * subject,const char * mail_body)906 int ulfius_send_smtp_email(const char * host,
907 const int port,
908 const int use_tls,
909 const int verify_certificate,
910 const char * user,
911 const char * password,
912 const char * from,
913 const char * to,
914 const char * cc,
915 const char * bcc,
916 const char * subject,
917 const char * mail_body) {
918 return ulfius_send_smtp_rich_email(host, port, use_tls, verify_certificate, user, password, from, to, cc, bcc, "text/plain; charset=utf-8", subject, mail_body);
919 }
920 #endif
921