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