aboutsummaryrefslogtreecommitdiff
path: root/src/curl/curl.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/curl/curl.c')
-rw-r--r--src/curl/curl.c898
1 files changed, 0 insertions, 898 deletions
diff --git a/src/curl/curl.c b/src/curl/curl.c
deleted file mode 100644
index ee4b568df..000000000
--- a/src/curl/curl.c
+++ /dev/null
@@ -1,898 +0,0 @@
1/*
2 This file is part of GNUnet
3 Copyright (C) 2014, 2015, 2016, 2018 GNUnet e.V.
4
5 GNUnet is free software: you can redistribute it and/or modify it
6 under the terms of the GNU Affero General Public License as published
7 by the Free Software Foundation, either version 3 of the License,
8 or (at your option) any later version.
9
10 GNUnet is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Affero General Public License for more details.
14
15 You should have received a copy of the GNU Affero General Public License
16 along with this program. If not, see <http://www.gnu.org/licenses/>.
17
18 SPDX-License-Identifier: AGPL3.0-or-later
19 */
20/**
21 * @file curl/curl.c
22 * @brief API for downloading JSON via CURL
23 * @author Sree Harsha Totakura <sreeharsha@totakura.in>
24 * @author Christian Grothoff
25 */
26#include "platform.h"
27#include <jansson.h>
28#include <microhttpd.h>
29#include "gnunet_curl_lib.h"
30
31#if ENABLE_BENCHMARK
32#include "../util/benchmark.h"
33#endif
34
35/**
36 * Set to 1 for extra debug logging.
37 */
38#define DEBUG 0
39
40/**
41 * Log error related to CURL operations.
42 *
43 * @param type log level
44 * @param function which function failed to run
45 * @param code what was the curl error code
46 */
47#define CURL_STRERROR(type, function, code) \
48 GNUNET_log (type, \
49 "Curl function `%s' has failed at `%s:%d' with error: %s\n", \
50 function, \
51 __FILE__, \
52 __LINE__, \
53 curl_easy_strerror (code));
54
55/**
56 * Print JSON parsing related error information
57 */
58#define JSON_WARN(error) \
59 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
60 "JSON parsing failed at %s:%u: %s (%s)\n", \
61 __FILE__, \
62 __LINE__, \
63 error.text, \
64 error.source)
65
66
67/**
68 * Failsafe flag. Raised if our constructor fails to initialize
69 * the Curl library.
70 */
71static int curl_fail;
72
73/**
74 * Jobs are CURL requests running within a `struct GNUNET_CURL_Context`.
75 */
76struct GNUNET_CURL_Job
77{
78 /**
79 * We keep jobs in a DLL.
80 */
81 struct GNUNET_CURL_Job *next;
82
83 /**
84 * We keep jobs in a DLL.
85 */
86 struct GNUNET_CURL_Job *prev;
87
88 /**
89 * Easy handle of the job.
90 */
91 CURL *easy_handle;
92
93 /**
94 * Context this job runs in.
95 */
96 struct GNUNET_CURL_Context *ctx;
97
98 /**
99 * Function to call upon completion.
100 */
101 GNUNET_CURL_JobCompletionCallback jcc;
102
103 /**
104 * Closure for @e jcc.
105 */
106 void *jcc_cls;
107
108 /**
109 * Function to call upon completion.
110 */
111 GNUNET_CURL_RawJobCompletionCallback jcc_raw;
112
113 /**
114 * Closure for @e jcc_raw.
115 */
116 void *jcc_raw_cls;
117
118 /**
119 * Buffer for response received from CURL.
120 */
121 struct GNUNET_CURL_DownloadBuffer db;
122
123 /**
124 * Headers used for this job, the list needs to be freed
125 * after the job has finished.
126 */
127 struct curl_slist *job_headers;
128
129 /**
130 * When did we start the job?
131 */
132 struct GNUNET_TIME_Absolute start_time;
133};
134
135
136/**
137 * Context
138 */
139struct GNUNET_CURL_Context
140{
141 /**
142 * Curl multi handle
143 */
144 CURLM *multi;
145
146 /**
147 * Curl share handle
148 */
149 CURLSH *share;
150
151 /**
152 * We keep jobs in a DLL.
153 */
154 struct GNUNET_CURL_Job *jobs_head;
155
156 /**
157 * We keep jobs in a DLL.
158 */
159 struct GNUNET_CURL_Job *jobs_tail;
160
161 /**
162 * Headers common for all requests in the context.
163 */
164 struct curl_slist *common_headers;
165
166 /**
167 * If non-NULL, the async scope ID is sent in a request
168 * header of this name.
169 */
170 const char *async_scope_id_header;
171
172 /**
173 * Function we need to call whenever the event loop's
174 * socket set changed.
175 */
176 GNUNET_CURL_RescheduleCallback cb;
177
178 /**
179 * Closure for @e cb.
180 */
181 void *cb_cls;
182
183 /**
184 * USERNAME:PASSWORD to use for client-authentication
185 * with all requests of this context, or NULL.
186 */
187 char *userpass;
188
189 /**
190 * Type of the TLS client certificate used, or NULL.
191 */
192 char *certtype;
193
194 /**
195 * File with the TLS client certificate, or NULL.
196 */
197 char *certfile;
198
199 /**
200 * File with the private key to authenticate the
201 * TLS client, or NULL.
202 */
203 char *keyfile;
204
205 /**
206 * Passphrase to decrypt @e keyfile, or NULL.
207 */
208 char *keypass;
209
210};
211
212
213void
214GNUNET_CURL_set_userpass (struct GNUNET_CURL_Context *ctx,
215 const char *userpass)
216{
217 GNUNET_free (ctx->userpass);
218 if (NULL != userpass)
219 ctx->userpass = GNUNET_strdup (userpass);
220}
221
222
223void
224GNUNET_CURL_set_tlscert (struct GNUNET_CURL_Context *ctx,
225 const char *certtype,
226 const char *certfile,
227 const char *keyfile,
228 const char *keypass)
229{
230 GNUNET_free (ctx->certtype);
231 GNUNET_free (ctx->certfile);
232 GNUNET_free (ctx->keyfile);
233 GNUNET_free (ctx->keypass);
234 if (NULL != certtype)
235 ctx->certtype = GNUNET_strdup (certtype);
236 if (NULL != certfile)
237 ctx->certfile = GNUNET_strdup (certfile);
238 if (NULL != keyfile)
239 ctx->certtype = GNUNET_strdup (keyfile);
240 if (NULL != keypass)
241 ctx->certtype = GNUNET_strdup (keypass);
242}
243
244
245struct GNUNET_CURL_Context *
246GNUNET_CURL_init (GNUNET_CURL_RescheduleCallback cb,
247 void *cb_cls)
248{
249 struct GNUNET_CURL_Context *ctx;
250 CURLM *multi;
251 CURLSH *share;
252
253 if (curl_fail)
254 {
255 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
256 "Curl was not initialised properly\n");
257 return NULL;
258 }
259 if (NULL == (multi = curl_multi_init ()))
260 {
261 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
262 "Failed to create a Curl multi handle\n");
263 return NULL;
264 }
265 if (NULL == (share = curl_share_init ()))
266 {
267 GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
268 "Failed to create a Curl share handle\n");
269 return NULL;
270 }
271 ctx = GNUNET_new (struct GNUNET_CURL_Context);
272 ctx->cb = cb;
273 ctx->cb_cls = cb_cls;
274 ctx->multi = multi;
275 ctx->share = share;
276 return ctx;
277}
278
279
280void
281GNUNET_CURL_enable_async_scope_header (struct GNUNET_CURL_Context *ctx,
282 const char *header_name)
283{
284 ctx->async_scope_id_header = header_name;
285}
286
287
288enum GNUNET_GenericReturnValue
289GNUNET_CURL_is_valid_scope_id (const char *scope_id)
290{
291 if (strlen (scope_id) >= 64)
292 return GNUNET_NO;
293 for (size_t i = 0; i < strlen (scope_id); i++)
294 if (! (isalnum (scope_id[i]) || (scope_id[i] == '-')))
295 return GNUNET_NO;
296 return GNUNET_YES;
297}
298
299
300/**
301 * Callback used when downloading the reply to an HTTP request.
302 * Just appends all of the data to the `buf` in the
303 * `struct DownloadBuffer` for further processing. The size of
304 * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
305 * the download exceeds this size, we abort with an error.
306 *
307 * @param bufptr data downloaded via HTTP
308 * @param size size of an item in @a bufptr
309 * @param nitems number of items in @a bufptr
310 * @param cls the `struct DownloadBuffer`
311 * @return number of bytes processed from @a bufptr
312 */
313static size_t
314download_cb (char *bufptr,
315 size_t size,
316 size_t nitems,
317 void *cls)
318{
319 struct GNUNET_CURL_DownloadBuffer *db = cls;
320 size_t msize;
321 void *buf;
322
323 if (0 == size * nitems)
324 {
325 /* Nothing (left) to do */
326 return 0;
327 }
328 msize = size * nitems;
329 if ((msize + db->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
330 {
331 db->eno = ENOMEM;
332 return 0; /* signals an error to curl */
333 }
334 db->buf = GNUNET_realloc (db->buf,
335 db->buf_size + msize);
336 buf = db->buf + db->buf_size;
337 GNUNET_memcpy (buf, bufptr, msize);
338 db->buf_size += msize;
339 return msize;
340}
341
342
343/**
344 * Create the HTTP headers for the request
345 *
346 * @param ctx context we run in
347 * @param job_headers job-specific headers
348 * @return all headers to use
349 */
350static struct curl_slist *
351setup_job_headers (struct GNUNET_CURL_Context *ctx,
352 const struct curl_slist *job_headers)
353{
354 struct curl_slist *all_headers = NULL;
355
356 for (const struct curl_slist *curr = job_headers;
357 NULL != curr;
358 curr = curr->next)
359 {
360 GNUNET_assert (NULL !=
361 (all_headers = curl_slist_append (all_headers,
362 curr->data)));
363 }
364
365 for (const struct curl_slist *curr = ctx->common_headers;
366 NULL != curr;
367 curr = curr->next)
368 {
369 GNUNET_assert (NULL !=
370 (all_headers = curl_slist_append (all_headers,
371 curr->data)));
372 }
373
374 if (NULL != ctx->async_scope_id_header)
375 {
376 struct GNUNET_AsyncScopeSave scope;
377
378 GNUNET_async_scope_get (&scope);
379 if (GNUNET_YES == scope.have_scope)
380 {
381 char *aid_header;
382
383 aid_header =
384 GNUNET_STRINGS_data_to_string_alloc (
385 &scope.scope_id,
386 sizeof(struct GNUNET_AsyncScopeId));
387 GNUNET_assert (NULL != aid_header);
388 GNUNET_assert (NULL != curl_slist_append (all_headers,
389 aid_header));
390 GNUNET_free (aid_header);
391 }
392 }
393 return all_headers;
394}
395
396
397/**
398 * Create a job.
399 *
400 * @param eh easy handle to use
401 * @param ctx context to run the job in
402 * @param all_headers HTTP client headers to use (free'd)
403 * @return NULL on error
404 */
405static struct GNUNET_CURL_Job *
406setup_job (CURL *eh,
407 struct GNUNET_CURL_Context *ctx,
408 struct curl_slist *all_headers)
409{
410 struct GNUNET_CURL_Job *job;
411
412 if (CURLE_OK !=
413 curl_easy_setopt (eh,
414 CURLOPT_HTTPHEADER,
415 all_headers))
416 {
417 GNUNET_break (0);
418 curl_slist_free_all (all_headers);
419 curl_easy_cleanup (eh);
420 return NULL;
421 }
422 job = GNUNET_new (struct GNUNET_CURL_Job);
423 job->start_time = GNUNET_TIME_absolute_get ();
424 job->job_headers = all_headers;
425
426 if ( (CURLE_OK !=
427 curl_easy_setopt (eh,
428 CURLOPT_PRIVATE,
429 job)) ||
430 (CURLE_OK !=
431 curl_easy_setopt (eh,
432 CURLOPT_WRITEFUNCTION,
433 &download_cb)) ||
434 (CURLE_OK !=
435 curl_easy_setopt (eh,
436 CURLOPT_WRITEDATA,
437 &job->db)) ||
438 (CURLE_OK !=
439 curl_easy_setopt (eh,
440 CURLOPT_SHARE,
441 ctx->share)) )
442 {
443 GNUNET_break (0);
444 GNUNET_free (job);
445 curl_easy_cleanup (eh);
446 return NULL;
447 }
448 if ( (CURLM_OK !=
449 curl_multi_add_handle (ctx->multi,
450 eh)) )
451 {
452 GNUNET_break (0);
453 GNUNET_free (job);
454 curl_easy_cleanup (eh);
455 return NULL;
456 }
457 job->easy_handle = eh;
458 job->ctx = ctx;
459 GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
460 ctx->jobs_tail,
461 job);
462 return job;
463}
464
465
466void
467GNUNET_CURL_extend_headers (struct GNUNET_CURL_Job *job,
468 const struct curl_slist *extra_headers)
469{
470 struct curl_slist *all_headers = job->job_headers;
471
472 for (const struct curl_slist *curr = extra_headers;
473 NULL != curr;
474 curr = curr->next)
475 {
476 GNUNET_assert (NULL !=
477 (all_headers = curl_slist_append (all_headers,
478 curr->data)));
479 }
480 job->job_headers = all_headers;
481}
482
483
484struct GNUNET_CURL_Job *
485GNUNET_CURL_job_add_raw (struct GNUNET_CURL_Context *ctx,
486 CURL *eh,
487 const struct curl_slist *job_headers,
488 GNUNET_CURL_RawJobCompletionCallback jcc,
489 void *jcc_cls)
490{
491 struct GNUNET_CURL_Job *job;
492 struct curl_slist *all_headers;
493
494 GNUNET_assert (NULL != jcc);
495 all_headers = setup_job_headers (ctx,
496 job_headers);
497 if (NULL == (job = setup_job (eh,
498 ctx,
499 all_headers)))
500 return NULL;
501 job->jcc_raw = jcc;
502 job->jcc_raw_cls = jcc_cls;
503 ctx->cb (ctx->cb_cls);
504 return job;
505}
506
507
508struct GNUNET_CURL_Job *
509GNUNET_CURL_job_add2 (struct GNUNET_CURL_Context *ctx,
510 CURL *eh,
511 const struct curl_slist *job_headers,
512 GNUNET_CURL_JobCompletionCallback jcc,
513 void *jcc_cls)
514{
515 struct GNUNET_CURL_Job *job;
516 struct curl_slist *all_headers;
517
518 GNUNET_assert (NULL != jcc);
519 if ( (NULL != ctx->userpass) &&
520 (0 != curl_easy_setopt (eh,
521 CURLOPT_USERPWD,
522 ctx->userpass)) )
523 return NULL;
524 if ( (NULL != ctx->certfile) &&
525 (0 != curl_easy_setopt (eh,
526 CURLOPT_SSLCERT,
527 ctx->certfile)) )
528 return NULL;
529 if ( (NULL != ctx->certtype) &&
530 (0 != curl_easy_setopt (eh,
531 CURLOPT_SSLCERTTYPE,
532 ctx->certtype)) )
533 return NULL;
534 if ( (NULL != ctx->keyfile) &&
535 (0 != curl_easy_setopt (eh,
536 CURLOPT_SSLKEY,
537 ctx->keyfile)) )
538 return NULL;
539 if ( (NULL != ctx->keypass) &&
540 (0 != curl_easy_setopt (eh,
541 CURLOPT_KEYPASSWD,
542 ctx->keypass)) )
543 return NULL;
544
545 all_headers = setup_job_headers (ctx,
546 job_headers);
547 if (NULL == (job = setup_job (eh,
548 ctx,
549 all_headers)))
550 return NULL;
551
552 job->jcc = jcc;
553 job->jcc_cls = jcc_cls;
554 ctx->cb (ctx->cb_cls);
555 return job;
556}
557
558
559struct GNUNET_CURL_Job *
560GNUNET_CURL_job_add_with_ct_json (struct GNUNET_CURL_Context *ctx,
561 CURL *eh,
562 GNUNET_CURL_JobCompletionCallback jcc,
563 void *jcc_cls)
564{
565 struct GNUNET_CURL_Job *job;
566 struct curl_slist *job_headers = NULL;
567
568 GNUNET_assert (NULL != (job_headers =
569 curl_slist_append (NULL,
570 "Content-Type: application/json")));
571 job = GNUNET_CURL_job_add2 (ctx,
572 eh,
573 job_headers,
574 jcc,
575 jcc_cls);
576 curl_slist_free_all (job_headers);
577 return job;
578}
579
580
581struct GNUNET_CURL_Job *
582GNUNET_CURL_job_add (struct GNUNET_CURL_Context *ctx,
583 CURL *eh,
584 GNUNET_CURL_JobCompletionCallback jcc,
585 void *jcc_cls)
586{
587 return GNUNET_CURL_job_add2 (ctx,
588 eh,
589 NULL,
590 jcc,
591 jcc_cls);
592}
593
594
595void
596GNUNET_CURL_job_cancel (struct GNUNET_CURL_Job *job)
597{
598 struct GNUNET_CURL_Context *ctx = job->ctx;
599
600 GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
601 ctx->jobs_tail,
602 job);
603 GNUNET_break (CURLM_OK ==
604 curl_multi_remove_handle (ctx->multi,
605 job->easy_handle));
606 curl_easy_cleanup (job->easy_handle);
607 GNUNET_free (job->db.buf);
608 curl_slist_free_all (job->job_headers);
609 ctx->cb (ctx->cb_cls);
610 GNUNET_free (job);
611}
612
613
614/**
615 * Test if the given content type @a ct is JSON
616 *
617 * @param ct a content type, e.g. "application/json; charset=UTF-8"
618 * @return true if @a ct denotes JSON
619 */
620static bool
621is_json (const char *ct)
622{
623 const char *semi;
624
625 /* check for "application/json" exact match */
626 if (0 == strcasecmp (ct,
627 "application/json"))
628 return true;
629 /* check for "application/json;[ANYTHING]" */
630 semi = strchr (ct,
631 ';');
632 /* also allow "application/json [ANYTHING]" (note the space!) */
633 if (NULL == semi)
634 semi = strchr (ct,
635 ' ');
636 if (NULL == semi)
637 return false; /* no delimiter we accept, forget it */
638 if (semi - ct != strlen ("application/json"))
639 return false; /* delimiter past desired length, forget it */
640 if (0 == strncasecmp (ct,
641 "application/json",
642 strlen ("application/json")))
643 return true; /* OK */
644 return false;
645}
646
647
648void *
649GNUNET_CURL_download_get_result_ (struct GNUNET_CURL_DownloadBuffer *db,
650 CURL *eh,
651 long *response_code)
652{
653 json_t *json;
654 json_error_t error;
655 char *ct;
656
657#if DEBUG
658 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
659 "Downloaded body: %.*s\n",
660 (int) db->buf_size,
661 (char *) db->buf);
662#endif
663 if (CURLE_OK !=
664 curl_easy_getinfo (eh,
665 CURLINFO_RESPONSE_CODE,
666 response_code))
667 {
668 /* unexpected error... */
669 GNUNET_break (0);
670 *response_code = 0;
671 }
672 if ((CURLE_OK !=
673 curl_easy_getinfo (eh,
674 CURLINFO_CONTENT_TYPE,
675 &ct)) ||
676 (NULL == ct) ||
677 (! is_json (ct)))
678 {
679 /* No content type or explicitly not JSON, refuse to parse
680 (but keep response code) */
681 if (0 != db->buf_size)
682 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
683 "Did NOT detect response `%.*s' as JSON\n",
684 (int) db->buf_size,
685 (const char *) db->buf);
686 return NULL;
687 }
688 if (MHD_HTTP_NO_CONTENT == *response_code)
689 return NULL;
690 if (0 == *response_code)
691 {
692 char *url;
693
694 if (CURLE_OK !=
695 curl_easy_getinfo (eh,
696 CURLINFO_EFFECTIVE_URL,
697 &url))
698 url = "<unknown URL>";
699 GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
700 "Failed to download response from `%s': \n",
701 url);
702 return NULL;
703 }
704 json = NULL;
705 if (0 == db->eno)
706 {
707 json = json_loadb (db->buf,
708 db->buf_size,
709 JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
710 &error);
711 if (NULL == json)
712 {
713 JSON_WARN (error);
714 *response_code = 0;
715 }
716 }
717 GNUNET_free (db->buf);
718 db->buf = NULL;
719 db->buf_size = 0;
720 return json;
721}
722
723
724enum GNUNET_GenericReturnValue
725GNUNET_CURL_append_header (struct GNUNET_CURL_Context *ctx,
726 const char *header)
727{
728 ctx->common_headers = curl_slist_append (ctx->common_headers,
729 header);
730 if (NULL == ctx->common_headers)
731 return GNUNET_SYSERR;
732
733 return GNUNET_OK;
734}
735
736
737void
738GNUNET_CURL_perform2 (struct GNUNET_CURL_Context *ctx,
739 GNUNET_CURL_RawParser rp,
740 GNUNET_CURL_ResponseCleaner rc)
741{
742 CURLMsg *cmsg;
743 int n_running;
744 int n_completed;
745
746 (void) curl_multi_perform (ctx->multi,
747 &n_running);
748 while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
749 &n_completed)))
750 {
751 struct GNUNET_CURL_Job *job;
752 struct GNUNET_TIME_Relative duration;
753 long response_code;
754 void *response;
755
756 /* Only documented return value is CURLMSG_DONE */
757 GNUNET_break (CURLMSG_DONE == cmsg->msg);
758 GNUNET_assert (CURLE_OK ==
759 curl_easy_getinfo (cmsg->easy_handle,
760 CURLINFO_PRIVATE,
761 (char **) &job));
762 GNUNET_assert (job->ctx == ctx);
763 response_code = 0;
764 duration = GNUNET_TIME_absolute_get_duration (job->start_time);
765 if (NULL != job->jcc_raw)
766 {
767 /* RAW mode, no parsing */
768 GNUNET_break (CURLE_OK ==
769 curl_easy_getinfo (job->easy_handle,
770 CURLINFO_RESPONSE_CODE,
771 &response_code));
772 job->jcc_raw (job->jcc_raw_cls,
773 response_code,
774 job->db.buf,
775 job->db.buf_size);
776 }
777 else
778 {
779 /* to be parsed via 'rp' */
780 response = rp (&job->db,
781 job->easy_handle,
782 &response_code);
783 job->jcc (job->jcc_cls,
784 response_code,
785 response);
786 rc (response);
787 }
788 {
789 char *url = NULL;
790
791 if (CURLE_UNKNOWN_OPTION ==
792 curl_easy_getinfo (job->easy_handle,
793 CURLINFO_EFFECTIVE_URL,
794 &url))
795 url = "<unknown>";
796 GNUNET_log (GNUNET_ERROR_TYPE_INFO,
797 "HTTP request for `%s' finished with %u after %s\n",
798 url,
799 (unsigned int) response_code,
800 GNUNET_TIME_relative2s (duration,
801 true));
802 /* Note: we MUST NOT free 'url' here */
803 }
804 GNUNET_CURL_job_cancel (job);
805 }
806}
807
808
809void
810GNUNET_CURL_perform (struct GNUNET_CURL_Context *ctx)
811{
812 GNUNET_CURL_perform2 (ctx,
813 &GNUNET_CURL_download_get_result_,
814 (GNUNET_CURL_ResponseCleaner) & json_decref);
815}
816
817
818void
819GNUNET_CURL_get_select_info (struct GNUNET_CURL_Context *ctx,
820 fd_set *read_fd_set,
821 fd_set *write_fd_set,
822 fd_set *except_fd_set,
823 int *max_fd,
824 long *timeout)
825{
826 long to;
827 int m;
828
829 m = -1;
830 GNUNET_assert (CURLM_OK ==
831 curl_multi_fdset (ctx->multi,
832 read_fd_set,
833 write_fd_set,
834 except_fd_set,
835 &m));
836 to = *timeout;
837 *max_fd = GNUNET_MAX (m, *max_fd);
838 GNUNET_assert (CURLM_OK ==
839 curl_multi_timeout (ctx->multi,
840 &to));
841
842 /* Only if what we got back from curl is smaller than what we
843 already had (-1 == infinity!), then update timeout */
844 if ((to < *timeout) && (-1 != to))
845 *timeout = to;
846 if ((-1 == (*timeout)) && (NULL != ctx->jobs_head))
847 *timeout = to;
848}
849
850
851void
852GNUNET_CURL_fini (struct GNUNET_CURL_Context *ctx)
853{
854 /* all jobs must have been cancelled at this time, assert this */
855 GNUNET_assert (NULL == ctx->jobs_head);
856 curl_share_cleanup (ctx->share);
857 curl_multi_cleanup (ctx->multi);
858 curl_slist_free_all (ctx->common_headers);
859 GNUNET_free (ctx->userpass);
860 GNUNET_free (ctx->certtype);
861 GNUNET_free (ctx->certfile);
862 GNUNET_free (ctx->keyfile);
863 GNUNET_free (ctx->keypass);
864 GNUNET_free (ctx);
865}
866
867
868/**
869 * Initial global setup logic, specifically runs the Curl setup.
870 */
871__attribute__ ((constructor)) void
872GNUNET_CURL_constructor__ (void)
873{
874 CURLcode ret;
875
876 if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
877 {
878 CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
879 "curl_global_init",
880 ret);
881 curl_fail = 1;
882 }
883}
884
885
886/**
887 * Cleans up after us, specifically runs the Curl cleanup.
888 */
889__attribute__ ((destructor)) void
890GNUNET_CURL_destructor__ (void)
891{
892 if (curl_fail)
893 return;
894 curl_global_cleanup ();
895}
896
897
898/* end of curl.c */