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