Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2014-2021 Taler Systems SA
4 :
5 : TALER is free software; you can redistribute it and/or modify it under the
6 : terms of the GNU Affero General Public License as published by the Free Software
7 : Foundation; either version 3, or (at your option) any later version.
8 :
9 : TALER is distributed in the hope that it will be useful, but WITHOUT ANY
10 : WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
11 : A PARTICULAR PURPOSE. See the GNU General Public License for more details.
12 :
13 : You should have received a copy of the GNU General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file taler-merchant-httpd_exchanges.c
18 : * @brief logic this HTTPD keeps for each exchange we interact with
19 : * @author Marcello Stanisci
20 : * @author Christian Grothoff
21 : */
22 : #include "platform.h"
23 : #include <taler/taler_json_lib.h>
24 : #include "taler-merchant-httpd_exchanges.h"
25 : #include "taler-merchant-httpd.h"
26 :
27 :
28 : /**
29 : * Delay after which we'll re-fetch key information from the exchange.
30 : */
31 : #define RELOAD_DELAY GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES, 2)
32 :
33 : /**
34 : * Delay after which we'll allow clients to force us to re-fetch key
35 : * information from the exchange if we don't know the denomination key.
36 : */
37 : #define FORCED_RELOAD_DELAY GNUNET_TIME_relative_multiply ( \
38 : GNUNET_TIME_UNIT_MINUTES, 15)
39 :
40 : /**
41 : * Threshold after which exponential backoff should not increase.
42 : */
43 : #define RETRY_BACKOFF_THRESHOLD GNUNET_TIME_relative_multiply ( \
44 : GNUNET_TIME_UNIT_SECONDS, 60)
45 :
46 :
47 : /**
48 : * Perform our exponential back-off calculation, starting at 1 ms
49 : * and then going by a factor of 2 up unto a maximum of RETRY_BACKOFF_THRESHOLD.
50 : *
51 : * @param r current backoff time, initially zero
52 : */
53 : #define RETRY_BACKOFF(r) GNUNET_TIME_relative_min (RETRY_BACKOFF_THRESHOLD, \
54 : GNUNET_TIME_relative_multiply ( \
55 : GNUNET_TIME_relative_max ( \
56 : GNUNET_TIME_UNIT_MILLISECONDS, \
57 : (r)), 2));
58 :
59 :
60 : /**
61 : * Exchange
62 : */
63 : struct Exchange;
64 :
65 :
66 : /**
67 : * Information we keep for a pending #MMH_EXCHANGES_find_exchange() operation.
68 : */
69 : struct TMH_EXCHANGES_FindOperation
70 : {
71 :
72 : /**
73 : * Kept in a DLL.
74 : */
75 : struct TMH_EXCHANGES_FindOperation *next;
76 :
77 : /**
78 : * Kept in a DLL.
79 : */
80 : struct TMH_EXCHANGES_FindOperation *prev;
81 :
82 : /**
83 : * Function to call with the result.
84 : */
85 : TMH_EXCHANGES_FindContinuation fc;
86 :
87 : /**
88 : * Closure for @e fc.
89 : */
90 : void *fc_cls;
91 :
92 : /**
93 : * Exchange we wait for the /keys for.
94 : */
95 : struct Exchange *my_exchange;
96 :
97 : /**
98 : * Wire method we care about for fees, NULL if we do not care about wire fees.
99 : */
100 : char *wire_method;
101 :
102 : /**
103 : * Task scheduled to asynchronously return the result to
104 : * the find continuation.
105 : */
106 : struct GNUNET_SCHEDULER_Task *at;
107 :
108 : };
109 :
110 :
111 : /**
112 : * Information about wire transfer fees of an exchange, by wire method.
113 : */
114 : struct FeesByWireMethod
115 : {
116 :
117 : /**
118 : * Kept in a DLL.
119 : */
120 : struct FeesByWireMethod *next;
121 :
122 : /**
123 : * Kept in a DLL.
124 : */
125 : struct FeesByWireMethod *prev;
126 :
127 : /**
128 : * Wire method these fees are for.
129 : */
130 : char *wire_method;
131 :
132 : /**
133 : * Full payto URI of the exchange.
134 : */
135 : char *payto_uri;
136 :
137 : /**
138 : * Applicable fees, NULL if unknown/error.
139 : */
140 : struct TALER_EXCHANGE_WireAggregateFees *af;
141 :
142 : };
143 :
144 :
145 : /**
146 : * Exchange
147 : */
148 : struct Exchange
149 : {
150 :
151 : /**
152 : * Kept in a DLL.
153 : */
154 : struct Exchange *next;
155 :
156 : /**
157 : * Kept in a DLL.
158 : */
159 : struct Exchange *prev;
160 :
161 : /**
162 : * Head of FOs pending for this exchange.
163 : */
164 : struct TMH_EXCHANGES_FindOperation *fo_head;
165 :
166 : /**
167 : * Tail of FOs pending for this exchange.
168 : */
169 : struct TMH_EXCHANGES_FindOperation *fo_tail;
170 :
171 : /**
172 : * (base) URL of the exchange.
173 : */
174 : char *url;
175 :
176 : /**
177 : * A connection to this exchange
178 : */
179 : struct TALER_EXCHANGE_Handle *conn;
180 :
181 : /**
182 : * Active /wire request to the exchange, or NULL.
183 : */
184 : struct TALER_EXCHANGE_WireHandle *wire_request;
185 :
186 : /**
187 : * Task to re-run /wire after some delay.
188 : */
189 : struct GNUNET_SCHEDULER_Task *wire_task;
190 :
191 : /**
192 : * Head of wire fees from /wire request.
193 : */
194 : struct FeesByWireMethod *wire_fees_head;
195 :
196 : /**
197 : * Tail of wire fees from /wire request.
198 : */
199 : struct FeesByWireMethod *wire_fees_tail;
200 :
201 : /**
202 : * Master public key, guaranteed to be set ONLY for
203 : * trusted exchanges.
204 : */
205 : struct TALER_MasterPublicKeyP master_pub;
206 :
207 : /**
208 : * How soon can may we, at the earliest, re-download /keys?
209 : */
210 : struct GNUNET_TIME_Absolute first_retry;
211 :
212 : /**
213 : * How long should we wait between the next retry?
214 : */
215 : struct GNUNET_TIME_Relative retry_delay;
216 :
217 : /**
218 : * How long should we wait between the next retry for /wire?
219 : */
220 : struct GNUNET_TIME_Relative wire_retry_delay;
221 :
222 : /**
223 : * Task where we retry fetching /keys from the exchange.
224 : */
225 : struct GNUNET_SCHEDULER_Task *retry_task;
226 :
227 : /**
228 : * true to indicate that there is an ongoing
229 : * transfer we are waiting for,
230 : * false to indicate that key data is up-to-date.
231 : */
232 : bool pending;
233 :
234 : /**
235 : * true if this exchange is from our configuration and
236 : * explicitly trusted, false if we need to check each
237 : * key to be sure it is trusted.
238 : */
239 : bool trusted;
240 :
241 : };
242 :
243 :
244 : /**
245 : * Context for all exchange operations (useful to the event loop)
246 : */
247 : static struct GNUNET_CURL_Context *merchant_curl_ctx;
248 :
249 : /**
250 : * Context for integrating #merchant_curl_ctx with the
251 : * GNUnet event loop.
252 : */
253 : static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
254 :
255 : /**
256 : * Head of exchanges we know about.
257 : */
258 : static struct Exchange *exchange_head;
259 :
260 : /**
261 : * Tail of exchanges we know about.
262 : */
263 : static struct Exchange *exchange_tail;
264 :
265 : /**
266 : * List of our trusted exchanges for inclusion in contracts.
267 : */
268 : json_t *TMH_trusted_exchanges;
269 :
270 :
271 : /**
272 : * Function called with information about who is auditing
273 : * a particular exchange and what key the exchange is using.
274 : *
275 : * @param cls closure, will be `struct Exchange` so that
276 : * when this function gets called, it will change the flag 'pending'
277 : * to 'false'. Note: 'keys' is automatically saved inside the exchange's
278 : * handle, which is contained inside 'struct Exchange', when
279 : * this callback is called. Thus, once 'pending' turns 'false',
280 : * it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
281 : * in order to get the "good" keys.
282 : * @param hr http response details
283 : * @param keys information about the various keys used
284 : * by the exchange
285 : * @param compat version compatibility data
286 : */
287 : static void
288 : keys_mgmt_cb (void *cls,
289 : const struct TALER_EXCHANGE_HttpResponse *hr,
290 : const struct TALER_EXCHANGE_Keys *keys,
291 : enum TALER_EXCHANGE_VersionCompatibility compat);
292 :
293 :
294 : /**
295 : * Retry getting information from the given exchange in
296 : * the closure.
297 : *
298 : * @param cls the exchange
299 : *
300 : */
301 : static void
302 0 : retry_exchange (void *cls)
303 : {
304 0 : struct Exchange *exchange = cls;
305 :
306 : /* might be a scheduled reload and not our first attempt */
307 0 : exchange->retry_task = NULL;
308 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
309 : "Connecting to exchange %s in retry_exchange()\n",
310 : exchange->url);
311 0 : if (NULL != exchange->conn)
312 : {
313 0 : TALER_EXCHANGE_disconnect (exchange->conn);
314 0 : exchange->conn = NULL;
315 : }
316 0 : exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx,
317 0 : exchange->url,
318 : &keys_mgmt_cb,
319 : exchange,
320 : TALER_EXCHANGE_OPTION_END);
321 : /* Note: while the API spec says 'returns NULL on error', the implementation
322 : actually never returns NULL. */
323 0 : GNUNET_break (NULL != exchange->conn);
324 0 : }
325 :
326 :
327 : /**
328 : * Function called with information about the wire fees
329 : * for each wire method. Stores the wire fees with the
330 : * exchange for later use.
331 : *
332 : * @param exchange connection to the exchange
333 : * @param master_pub public key of the exchange
334 : * @param wire_method name of the wire method (i.e. "iban")
335 : * @param payto_uri full payto URI of the exchange
336 : * @param fees fee structure for this method
337 : * @return #TALER_EC_NONE on success
338 : */
339 : static enum TALER_ErrorCode
340 0 : process_wire_fees (struct Exchange *exchange,
341 : const struct TALER_MasterPublicKeyP *master_pub,
342 : const char *wire_method,
343 : const char *payto_uri,
344 : const struct TALER_EXCHANGE_WireAggregateFees *fees)
345 : {
346 : struct FeesByWireMethod *f;
347 : struct TALER_EXCHANGE_WireAggregateFees *endp;
348 : struct TALER_EXCHANGE_WireAggregateFees *af;
349 :
350 0 : for (f = exchange->wire_fees_head; NULL != f; f = f->next)
351 0 : if (0 == strcasecmp (wire_method,
352 0 : f->wire_method))
353 0 : break;
354 0 : if (NULL == f)
355 : {
356 0 : f = GNUNET_new (struct FeesByWireMethod);
357 0 : f->wire_method = GNUNET_strdup (wire_method);
358 0 : f->payto_uri = GNUNET_strdup (payto_uri);
359 0 : GNUNET_CONTAINER_DLL_insert (exchange->wire_fees_head,
360 : exchange->wire_fees_tail,
361 : f);
362 : }
363 0 : endp = f->af;
364 0 : while ( (NULL != endp) &&
365 0 : (NULL != endp->next) )
366 0 : endp = endp->next;
367 0 : while ( (NULL != endp) &&
368 0 : (NULL != fees) &&
369 0 : (GNUNET_TIME_timestamp_cmp (fees->start_date,
370 : <,
371 : endp->end_date)) )
372 0 : fees = fees->next;
373 0 : if ( (NULL != endp) &&
374 0 : (NULL != fees) &&
375 0 : (GNUNET_TIME_timestamp_cmp (fees->start_date,
376 : !=,
377 : endp->end_date)) )
378 : {
379 : /* Hole in the fee structure, not allowed! */
380 0 : GNUNET_break_op (0);
381 0 : return TALER_EC_MERCHANT_GENERIC_HOLE_IN_WIRE_FEE_STRUCTURE;
382 : }
383 0 : while (NULL != fees)
384 : {
385 : struct GNUNET_HashCode h_wire_method;
386 : enum GNUNET_DB_QueryStatus qs;
387 :
388 0 : af = GNUNET_new (struct TALER_EXCHANGE_WireAggregateFees);
389 0 : *af = *fees;
390 0 : GNUNET_CRYPTO_hash (wire_method,
391 0 : strlen (wire_method) + 1,
392 : &h_wire_method);
393 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
394 : "Storing wire fee for `%s' and method `%s' at %s in DB; the fee is %s\n",
395 : TALER_B2S (master_pub),
396 : wire_method,
397 : GNUNET_TIME_timestamp2s (af->start_date),
398 : TALER_amount2s (&af->fees.wire));
399 0 : TMH_db->preflight (TMH_db->cls);
400 0 : if (GNUNET_OK !=
401 0 : TMH_db->start (TMH_db->cls,
402 : "store wire fee"))
403 : {
404 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
405 : "Failed to start database transaction to store exchange wire fees (will try to continue anyway)!\n");
406 0 : GNUNET_free (af);
407 0 : fees = fees->next;
408 0 : continue;
409 : }
410 0 : qs = TMH_db->store_wire_fee_by_exchange (TMH_db->cls,
411 : master_pub,
412 : &h_wire_method,
413 0 : &af->fees,
414 : af->start_date,
415 : af->end_date,
416 0 : &af->master_sig);
417 0 : if (0 > qs)
418 : {
419 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
420 : "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
421 0 : GNUNET_free (af);
422 0 : fees = fees->next;
423 0 : TMH_db->rollback (TMH_db->cls);
424 0 : continue;
425 : }
426 0 : if (0 == qs)
427 : {
428 : /* Entry was already in DB, fine, continue as if we had succeeded */
429 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
430 : "Fees already in DB, rolling back transaction attempt!\n");
431 0 : TMH_db->rollback (TMH_db->cls);
432 : }
433 0 : if (0 < qs)
434 : {
435 : /* Inserted into DB, make sure transaction completes */
436 0 : qs = TMH_db->commit (TMH_db->cls);
437 0 : if (0 > qs)
438 : {
439 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
440 : "Failed to persist exchange wire fees in merchant DB (will try to continue anyway)!\n");
441 0 : GNUNET_free (af);
442 0 : fees = fees->next;
443 0 : continue;
444 : }
445 : }
446 0 : af->next = NULL;
447 0 : if (NULL == endp)
448 0 : f->af = af;
449 : else
450 0 : endp->next = af;
451 0 : endp = af;
452 0 : fees = fees->next;
453 : }
454 0 : return TALER_EC_NONE;
455 : }
456 :
457 :
458 : /**
459 : * Function called with information about the wire accounts
460 : * of the exchange. Stores the wire fees with the
461 : * exchange for laster use.
462 : *
463 : * @param exchange the exchange
464 : * @param master_pub public key of the exchange
465 : * @param accounts_len length of the @a accounts array
466 : * @param accounts list of wire accounts of the exchange
467 : * @return #TALER_EC_NONE on success
468 : */
469 : static enum TALER_ErrorCode
470 0 : process_wire_accounts (struct Exchange *exchange,
471 : const struct TALER_MasterPublicKeyP *master_pub,
472 : unsigned int accounts_len,
473 : const struct TALER_EXCHANGE_WireAccount *accounts)
474 : {
475 0 : for (unsigned int i = 0; i<accounts_len; i++)
476 : {
477 : enum TALER_ErrorCode ec;
478 : char *method;
479 :
480 0 : method = TALER_payto_get_method (accounts[i].payto_uri);
481 0 : if (NULL == method)
482 : {
483 : /* malformed payto:// URI returned by exchange */
484 0 : GNUNET_break_op (0);
485 0 : return TALER_EC_GENERIC_PAYTO_URI_MALFORMED;
486 : }
487 0 : ec = process_wire_fees (exchange,
488 : master_pub,
489 : method,
490 0 : accounts[i].payto_uri,
491 0 : accounts[i].fees);
492 0 : GNUNET_free (method);
493 0 : if (TALER_EC_NONE != ec)
494 0 : return ec;
495 : }
496 0 : return TALER_EC_NONE;
497 : }
498 :
499 :
500 : /**
501 : * Obtain applicable fees for @a exchange and @a wire_method.
502 : *
503 : * @param exchange the exchange to query
504 : * @param now current time
505 : * @param wire_method the wire method we want the fees for
506 : * @return NULL if we do not have fees for this method yet
507 : */
508 : static const struct FeesByWireMethod *
509 0 : get_wire_fees (struct Exchange *exchange,
510 : struct GNUNET_TIME_Timestamp now,
511 : const char *wire_method)
512 : {
513 0 : for (struct FeesByWireMethod *fbw = exchange->wire_fees_head;
514 : NULL != fbw;
515 0 : fbw = fbw->next)
516 : {
517 0 : if (0 == strcasecmp (fbw->wire_method,
518 : wire_method) )
519 : {
520 : struct TALER_EXCHANGE_WireAggregateFees *af;
521 :
522 : /* Advance through list up to current time */
523 0 : while ( (NULL != (af = fbw->af)) &&
524 0 : (GNUNET_TIME_timestamp_cmp (now,
525 : >=,
526 : af->end_date)) )
527 : {
528 0 : fbw->af = af->next;
529 0 : GNUNET_free (af);
530 : }
531 0 : return fbw;
532 : }
533 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
534 : "Exchange supports `%s' as a wire method (but we do not use that one)\n",
535 : fbw->wire_method);
536 : }
537 0 : return NULL;
538 : }
539 :
540 :
541 : /**
542 : * Check if we have any remaining pending requests for the
543 : * given @a exchange, and if we have the required data, call
544 : * the callback.
545 : *
546 : * @param exchange the exchange to check for pending find operations
547 : * @return true if we need /wire data from @a exchange
548 : */
549 : static bool
550 0 : process_find_operations (struct Exchange *exchange)
551 : {
552 : struct TMH_EXCHANGES_FindOperation *fn;
553 : struct GNUNET_TIME_Timestamp now;
554 : bool need_wire;
555 :
556 0 : now = GNUNET_TIME_timestamp_get ();
557 0 : need_wire = false;
558 0 : for (struct TMH_EXCHANGES_FindOperation *fo = exchange->fo_head;
559 : NULL != fo;
560 0 : fo = fn)
561 : {
562 : const struct FeesByWireMethod *fbw;
563 :
564 0 : fn = fo->next;
565 0 : if (NULL != fo->wire_method)
566 : {
567 : /* Find fee structure for our wire method */
568 0 : fbw = get_wire_fees (exchange,
569 : now,
570 0 : fo->wire_method);
571 0 : if (NULL == fbw)
572 : {
573 0 : need_wire = true;
574 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
575 : "Missing wire fees for exchange %s and method %s\n",
576 : exchange->url,
577 : fo->wire_method);
578 : /* Do not warn if this is before our first attempt */
579 0 : if (! GNUNET_TIME_relative_is_zero (exchange->wire_retry_delay))
580 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
581 : "Exchange does not support `%s' wire method (will retry later)\n",
582 : fo->wire_method);
583 0 : fbw = NULL;
584 0 : continue;
585 : }
586 0 : if (NULL == fbw->af)
587 : {
588 : /* Disagreement on the current time */
589 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
590 : "Exchange has no wire fees configured for `%s' wire method (will retry later)\n",
591 : fo->wire_method);
592 0 : fbw = NULL;
593 0 : continue;
594 : }
595 0 : if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date,
596 : >,
597 : now))
598 : {
599 : /* Disagreement on the current time */
600 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
601 : "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n",
602 : GNUNET_TIME_relative2s (
603 : GNUNET_TIME_absolute_get_remaining (
604 : fbw->af->start_date.abs_time),
605 : true));
606 0 : fbw = NULL;
607 0 : continue;
608 : }
609 : }
610 : else
611 : {
612 : /* no wire transfer method given, so we yield no fee */
613 0 : fbw = NULL;
614 : }
615 : {
616 0 : struct TALER_EXCHANGE_HttpResponse hr = {
617 : .http_status = MHD_HTTP_OK,
618 : };
619 :
620 0 : fo->fc (fo->fc_cls,
621 : &hr,
622 : exchange->conn,
623 : (NULL != fbw) ? fbw->payto_uri : NULL,
624 0 : (NULL != fbw) ? &fbw->af->fees.wire : NULL,
625 0 : exchange->trusted);
626 : }
627 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
628 : }
629 0 : return need_wire;
630 : }
631 :
632 :
633 : static void
634 : wire_task_cb (void *cls);
635 :
636 :
637 : /**
638 : * Callbacks of this type are used to serve the result of submitting a
639 : * wire format inquiry request to a exchange.
640 : *
641 : * If the request fails to generate a valid response from the
642 : * exchange, @a http_status will also be zero.
643 : *
644 : * Must only be called if 'exchange->pending' is #GNUNET_NO,
645 : * that is #TALER_EXCHANGE_get_keys() will succeed.
646 : *
647 : * @param cls closure, a `struct Exchange`
648 : * @param hr HTTP response details
649 : * @param accounts_len length of the @a accounts array
650 : * @param accounts list of wire accounts of the exchange, NULL on error
651 : */
652 : static void
653 0 : handle_wire_data (void *cls,
654 : const struct TALER_EXCHANGE_HttpResponse *hr,
655 : unsigned int accounts_len,
656 : const struct TALER_EXCHANGE_WireAccount *accounts)
657 : {
658 0 : struct Exchange *exchange = cls;
659 : const struct TALER_EXCHANGE_Keys *keys;
660 : enum TALER_ErrorCode ecx;
661 :
662 0 : exchange->wire_request = NULL;
663 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
664 : "Received /wire response\n");
665 0 : if (MHD_HTTP_OK != hr->http_status)
666 : {
667 : struct TMH_EXCHANGES_FindOperation *fo;
668 :
669 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
670 : "Failed to obtain /wire details from `%s': %u/%d\n",
671 : exchange->url,
672 : hr->http_status,
673 : hr->ec);
674 0 : while (NULL != (fo = exchange->fo_head))
675 : {
676 0 : fo->fc (fo->fc_cls,
677 : hr,
678 : exchange->conn,
679 : NULL,
680 : NULL,
681 : GNUNET_NO);
682 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
683 : }
684 0 : return;
685 : }
686 0 : keys = TALER_EXCHANGE_get_keys (exchange->conn);
687 0 : GNUNET_assert (NULL != keys);
688 0 : ecx = process_wire_accounts (exchange,
689 : &keys->master_pub,
690 : accounts_len,
691 : accounts);
692 0 : if (TALER_EC_NONE != ecx)
693 : {
694 : /* Report hard failure to all callbacks! */
695 : struct TMH_EXCHANGES_FindOperation *fo;
696 0 : struct TALER_EXCHANGE_HttpResponse hrx = {
697 : .ec = ecx,
698 : .http_status = 0,
699 0 : .reply = hr->reply
700 : };
701 :
702 0 : GNUNET_break_op (0);
703 0 : while (NULL != (fo = exchange->fo_head))
704 : {
705 0 : fo->fc (fo->fc_cls,
706 : &hrx,
707 : NULL,
708 : NULL,
709 : NULL,
710 : GNUNET_NO);
711 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
712 : }
713 0 : return;
714 : }
715 0 : if ( (process_find_operations (exchange)) &&
716 0 : (NULL == exchange->wire_task) &&
717 0 : (NULL == exchange->wire_request) )
718 : {
719 : /* need to run /wire again. But as we DID get a successful reply,
720 : and as the exchange is unlikely to offer new wire methods very
721 : frequently, start with some significant delay */
722 : exchange->wire_retry_delay
723 0 : = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MINUTES,
724 : exchange->wire_retry_delay);
725 0 : exchange->wire_retry_delay = RETRY_BACKOFF (exchange->wire_retry_delay);
726 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
727 : "Exchange does not support our wire method. Retrying in %s\n",
728 :
729 : GNUNET_STRINGS_relative_time_to_string (
730 : exchange->wire_retry_delay,
731 : GNUNET_YES));
732 : exchange->wire_task
733 0 : = GNUNET_SCHEDULER_add_delayed (exchange->wire_retry_delay,
734 : &wire_task_cb,
735 : exchange);
736 : }
737 : }
738 :
739 :
740 : /**
741 : * Check if we have any remaining pending requests for the
742 : * given @a exchange, and if we have the required data, call
743 : * the callback. If requests without /wire data remain,
744 : * retry the /wire request after some delay.
745 : *
746 : * Must only be called if 'exchange->pending' is #GNUNET_NO,
747 : * that is #TALER_EXCHANGE_get_keys() will succeed.
748 : *
749 : * @param cls a `struct Exchange` to check
750 : */
751 : static void
752 0 : wire_task_cb (void *cls)
753 : {
754 0 : struct Exchange *exchange = cls;
755 :
756 0 : exchange->wire_task = NULL;
757 0 : GNUNET_assert (! exchange->pending);
758 0 : if (! process_find_operations (exchange))
759 0 : return; /* no more need */
760 0 : GNUNET_assert (NULL == exchange->wire_request);
761 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
762 : "Initiating /wire download\n");
763 0 : exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
764 : &handle_wire_data,
765 : exchange);
766 : }
767 :
768 :
769 : /**
770 : * Free @a exchange.
771 : *
772 : * @param[in] exchange entry to free
773 : */
774 : static void
775 0 : free_exchange_entry (struct Exchange *exchange)
776 : {
777 : struct FeesByWireMethod *f;
778 :
779 0 : GNUNET_CONTAINER_DLL_remove (exchange_head,
780 : exchange_tail,
781 : exchange);
782 0 : while (NULL != (f = exchange->wire_fees_head))
783 : {
784 : struct TALER_EXCHANGE_WireAggregateFees *af;
785 :
786 0 : GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head,
787 : exchange->wire_fees_tail,
788 : f);
789 0 : while (NULL != (af = f->af))
790 : {
791 0 : f->af = af->next;
792 0 : GNUNET_free (af);
793 : }
794 0 : GNUNET_free (f->wire_method);
795 0 : GNUNET_free (f->payto_uri);
796 0 : GNUNET_free (f);
797 : }
798 0 : if (NULL != exchange->wire_request)
799 : {
800 0 : TALER_EXCHANGE_wire_cancel (exchange->wire_request);
801 0 : exchange->wire_request = NULL;
802 : }
803 0 : if (NULL != exchange->wire_task)
804 : {
805 0 : GNUNET_SCHEDULER_cancel (exchange->wire_task);
806 0 : exchange->wire_task = NULL;
807 : }
808 0 : if (NULL != exchange->conn)
809 : {
810 0 : TALER_EXCHANGE_disconnect (exchange->conn);
811 0 : exchange->conn = NULL;
812 : }
813 0 : if (NULL != exchange->retry_task)
814 : {
815 0 : GNUNET_SCHEDULER_cancel (exchange->retry_task);
816 0 : exchange->retry_task = NULL;
817 : }
818 0 : GNUNET_assert (NULL == exchange->fo_head);
819 0 : GNUNET_assert (NULL == exchange->fo_tail);
820 0 : GNUNET_free (exchange->url);
821 0 : GNUNET_free (exchange);
822 0 : }
823 :
824 :
825 : /**
826 : * We failed downloading /keys from @a exchange. Tell clients
827 : * about our failure, abort pending operations and retry later.
828 : *
829 : * @param exchange exchange that failed
830 : * @param hr details about the HTTP reply
831 : * @param compat version compatibility data
832 : */
833 : static void
834 0 : fail_and_retry (struct Exchange *exchange,
835 : const struct TALER_EXCHANGE_HttpResponse *hr,
836 : enum TALER_EXCHANGE_VersionCompatibility compat)
837 : {
838 : struct TMH_EXCHANGES_FindOperation *fo;
839 :
840 0 : exchange->pending = true;
841 0 : if (NULL != exchange->wire_request)
842 : {
843 0 : TALER_EXCHANGE_wire_cancel (exchange->wire_request);
844 0 : exchange->wire_request = NULL;
845 : }
846 0 : if (NULL != exchange->wire_task)
847 : {
848 0 : GNUNET_SCHEDULER_cancel (exchange->wire_task);
849 0 : exchange->wire_task = NULL;
850 : }
851 0 : while (NULL != (fo = exchange->fo_head))
852 : {
853 0 : fo->fc (fo->fc_cls,
854 : hr,
855 : NULL,
856 : NULL,
857 : NULL,
858 : GNUNET_NO);
859 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
860 : }
861 0 : if (TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER == compat)
862 : {
863 : /* Log hard error: we likely need admin help! */
864 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
865 : "Exchange `%s' runs an incompatible more recent version of the Taler protocol. Will not retry. This client may need to be updated.\n",
866 : exchange->url);
867 : /* Theoretically, the exchange could downgrade,
868 : but let's not be too aggressive about retries
869 : on this one. */
870 0 : exchange->retry_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_HOURS,
871 : exchange->retry_delay);
872 : }
873 0 : if ( (NULL == exchange->fo_head) &&
874 0 : (TALER_EC_GENERIC_CONFIGURATION_INVALID == hr->ec) )
875 : {
876 : /* This can NEVER work, so don't retry */
877 0 : free_exchange_entry (exchange);
878 0 : return;
879 : }
880 0 : exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
881 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
882 : "Failed to fetch /keys from `%s': %d/%u, retrying in %s\n",
883 : exchange->url,
884 : (int) hr->ec,
885 : hr->http_status,
886 : GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay,
887 : GNUNET_YES));
888 0 : if (NULL != exchange->retry_task)
889 0 : GNUNET_SCHEDULER_cancel (exchange->retry_task);
890 0 : exchange->first_retry = GNUNET_TIME_relative_to_absolute (
891 : exchange->retry_delay);
892 0 : exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
893 : &retry_exchange,
894 : exchange);
895 : }
896 :
897 :
898 : /**
899 : * Function called with information about who is auditing
900 : * a particular exchange and what key the exchange is using.
901 : *
902 : * @param cls closure, will be `struct Exchange` so that
903 : * when this function gets called, it will change the flag 'pending'
904 : * to 'false'. Note: 'keys' is automatically saved inside the exchange's
905 : * handle, which is contained inside 'struct Exchange', when
906 : * this callback is called. Thus, once 'pending' turns 'false',
907 : * it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
908 : * in order to get the "good" keys.
909 : * @param hr http response details
910 : * @param keys information about the various keys used
911 : * by the exchange
912 : * @param compat version compatibility data
913 : */
914 : static void
915 0 : keys_mgmt_cb (void *cls,
916 : const struct TALER_EXCHANGE_HttpResponse *hr,
917 : const struct TALER_EXCHANGE_Keys *keys,
918 : enum TALER_EXCHANGE_VersionCompatibility compat)
919 : {
920 0 : struct Exchange *exchange = cls;
921 : struct GNUNET_TIME_Timestamp expire;
922 : struct GNUNET_TIME_Relative delay;
923 :
924 0 : if ( (MHD_HTTP_OK != hr->http_status) ||
925 : (NULL == keys) )
926 : {
927 0 : fail_and_retry (exchange,
928 : hr,
929 : compat);
930 0 : return;
931 : }
932 0 : if ( (exchange->trusted) &&
933 0 : (0 != GNUNET_memcmp (&exchange->master_pub,
934 : &keys->master_pub)) )
935 : {
936 : /* master pub differs => do not trust the exchange (without auditor) */
937 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
938 : "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n",
939 : exchange->url);
940 0 : exchange->trusted = false;
941 : }
942 0 : if (! exchange->trusted)
943 : {
944 0 : exchange->master_pub = keys->master_pub;
945 0 : for (struct Exchange *e = exchange_head;
946 : NULL != e;
947 0 : e = e->next)
948 : {
949 0 : if (e == exchange)
950 0 : continue;
951 0 : if (! e->trusted)
952 0 : continue;
953 0 : if (0 ==
954 0 : GNUNET_memcmp (&e->master_pub,
955 : &exchange->master_pub))
956 0 : exchange->trusted = true; /* same exchange, different URL => trust applies */
957 : }
958 : }
959 0 : if (0 != (TALER_EXCHANGE_VC_NEWER & compat))
960 : {
961 : /* Warn user exactly once about need to upgrade */
962 : static int once;
963 :
964 0 : if (0 == once)
965 : {
966 0 : once = 1;
967 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
968 : "Exchange `%s' runs a more recent version of the Taler protocol. You may want to update this client.\n",
969 : exchange->url);
970 : }
971 : }
972 :
973 : /* store exchange online signing keys in our DB */
974 0 : for (unsigned int i = 0; i<keys->num_sign_keys; i++)
975 : {
976 0 : struct TALER_EXCHANGE_SigningPublicKey *sign_key = &keys->sign_keys[i];
977 : enum GNUNET_DB_QueryStatus qs;
978 :
979 0 : TMH_db->preflight (TMH_db->cls);
980 0 : qs = TMH_db->insert_exchange_signkey (TMH_db->cls,
981 : &keys->master_pub,
982 0 : &sign_key->key,
983 : sign_key->valid_from,
984 : sign_key->valid_until,
985 : sign_key->valid_legal,
986 0 : &sign_key->master_sig);
987 : /* 0 is OK, we may already have the key in the DB! */
988 0 : if (0 > qs)
989 : {
990 0 : GNUNET_break (0);
991 0 : fail_and_retry (exchange,
992 : hr,
993 : compat);
994 0 : return;
995 : }
996 : }
997 :
998 0 : exchange->first_retry = GNUNET_TIME_relative_to_absolute (RELOAD_DELAY);
999 0 : expire = TALER_EXCHANGE_check_keys_current (exchange->conn,
1000 : TALER_EXCHANGE_CKF_NONE);
1001 0 : if (0 == GNUNET_TIME_absolute_is_zero (expire.abs_time))
1002 0 : delay = RELOAD_DELAY;
1003 : else
1004 0 : delay = GNUNET_TIME_absolute_get_remaining (expire.abs_time);
1005 0 : if (GNUNET_TIME_relative_is_zero (delay))
1006 : {
1007 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1008 : "/keys response from exchange expired immediately! Retrying in 1 minute.\n");
1009 0 : delay = GNUNET_TIME_UNIT_MINUTES;
1010 : }
1011 0 : exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
1012 0 : if (NULL != exchange->retry_task)
1013 0 : GNUNET_SCHEDULER_cancel (exchange->retry_task);
1014 : exchange->retry_task
1015 0 : = GNUNET_SCHEDULER_add_delayed (delay,
1016 : &retry_exchange,
1017 : exchange);
1018 0 : exchange->pending = false;
1019 0 : if ( (process_find_operations (exchange)) &&
1020 0 : (NULL == exchange->wire_request) &&
1021 0 : (NULL == exchange->wire_task) )
1022 : {
1023 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1024 : "Got key data, but also need wire data. Will request /wire now\n");
1025 0 : exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
1026 : &handle_wire_data,
1027 : exchange);
1028 : }
1029 : }
1030 :
1031 :
1032 : /**
1033 : * Task to return find operation result asynchronously to caller.
1034 : *
1035 : * @param cls a `struct TMH_EXCHANGES_FindOperation`
1036 : */
1037 : static void
1038 0 : return_result (void *cls)
1039 : {
1040 0 : struct TMH_EXCHANGES_FindOperation *fo = cls;
1041 0 : struct Exchange *exchange = fo->my_exchange;
1042 :
1043 0 : fo->at = NULL;
1044 0 : if ( (process_find_operations (exchange)) &&
1045 0 : (NULL == exchange->wire_request) &&
1046 0 : (! exchange->pending) &&
1047 0 : (NULL != exchange->wire_task) )
1048 : {
1049 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1050 : "Do not have current wire data. Will re-request /wire in 1 minute\n");
1051 : exchange->wire_task
1052 0 : = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1053 : &wire_task_cb,
1054 : exchange);
1055 : }
1056 0 : }
1057 :
1058 :
1059 : struct TMH_EXCHANGES_FindOperation *
1060 0 : TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
1061 : const char *wire_method,
1062 : int force_reload,
1063 : TMH_EXCHANGES_FindContinuation fc,
1064 : void *fc_cls)
1065 : {
1066 : struct Exchange *exchange;
1067 : struct TMH_EXCHANGES_FindOperation *fo;
1068 : struct GNUNET_TIME_Timestamp now;
1069 :
1070 0 : if (NULL == merchant_curl_ctx)
1071 : {
1072 0 : GNUNET_break (0);
1073 0 : return NULL;
1074 : }
1075 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1076 : "Trying to find chosen exchange `%s'\n",
1077 : chosen_exchange);
1078 : /* Check if the exchange is known */
1079 0 : for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
1080 : {
1081 : /* test it by checking URL */
1082 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1083 : "Comparing chosen exchange url '%s' with known url '%s'.\n",
1084 : chosen_exchange,
1085 : exchange->url);
1086 0 : if (0 == strcmp (exchange->url,
1087 : chosen_exchange))
1088 : {
1089 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1090 : "The exchange `%s' is already known (good)\n",
1091 : chosen_exchange);
1092 0 : break;
1093 : }
1094 : }
1095 0 : if (NULL == exchange)
1096 : {
1097 : /* This is a new exchange */
1098 0 : exchange = GNUNET_new (struct Exchange);
1099 0 : exchange->url = GNUNET_strdup (chosen_exchange);
1100 0 : exchange->pending = true;
1101 0 : GNUNET_CONTAINER_DLL_insert (exchange_head,
1102 : exchange_tail,
1103 : exchange);
1104 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1105 : "The exchange `%s' is new\n",
1106 : chosen_exchange);
1107 : }
1108 :
1109 0 : fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation);
1110 0 : fo->fc = fc;
1111 0 : fo->fc_cls = fc_cls;
1112 0 : fo->my_exchange = exchange;
1113 0 : if (NULL != wire_method)
1114 0 : fo->wire_method = GNUNET_strdup (wire_method);
1115 0 : GNUNET_CONTAINER_DLL_insert (exchange->fo_head,
1116 : exchange->fo_tail,
1117 : fo);
1118 0 : if ( (force_reload) &&
1119 0 : (GNUNET_TIME_absolute_is_past (exchange->first_retry)) )
1120 : {
1121 : /* increment exponential-backoff */
1122 0 : exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
1123 : /* do not allow forced check until both backoff and #FORCED_RELOAD_DELAY
1124 : are satisfied again */
1125 : exchange->first_retry
1126 0 : = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_max (
1127 : exchange->retry_delay,
1128 : FORCED_RELOAD_DELAY));
1129 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1130 : "/keys retry forced, waiting until %s\n",
1131 : GNUNET_TIME_absolute2s (exchange->first_retry));
1132 : /* NOTE: return value tells us how long /keys should still
1133 : be valid. */
1134 0 : (void) TALER_EXCHANGE_check_keys_current (exchange->conn,
1135 : TALER_EXCHANGE_CKF_FORCE_DOWNLOAD);
1136 0 : return fo;
1137 : }
1138 :
1139 0 : now = GNUNET_TIME_timestamp_get ();
1140 0 : if ( (! exchange->pending) &&
1141 0 : ( (NULL == fo->wire_method) ||
1142 0 : (NULL != get_wire_fees (exchange,
1143 : now,
1144 0 : fo->wire_method)) ) )
1145 : {
1146 : /* We are not currently waiting for a reply, immediately
1147 : return result */
1148 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1149 : "The exchange `%s' is ready\n",
1150 : chosen_exchange);
1151 0 : GNUNET_assert (NULL == fo->at);
1152 0 : fo->at = GNUNET_SCHEDULER_add_now (&return_result,
1153 : fo);
1154 0 : return fo;
1155 : }
1156 :
1157 : /* If new or resumed, (re)try fetching /keys */
1158 0 : if ( (NULL == exchange->conn) &&
1159 0 : (NULL == exchange->retry_task) &&
1160 0 : (exchange->pending) )
1161 : {
1162 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1163 : "Do not have current /keys data for `%s'. Will request /keys now\n",
1164 : chosen_exchange);
1165 0 : exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
1166 : exchange);
1167 : }
1168 0 : else if ( (! exchange->pending) &&
1169 0 : (NULL == exchange->wire_task) &&
1170 0 : (NULL == exchange->wire_request) )
1171 : {
1172 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1173 : "Do not have required wire data. Will re-request /wire now\n");
1174 0 : exchange->wire_task = GNUNET_SCHEDULER_add_now (&wire_task_cb,
1175 : exchange);
1176 : }
1177 0 : return fo;
1178 : }
1179 :
1180 :
1181 : void
1182 0 : TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo)
1183 : {
1184 0 : struct Exchange *exchange = fo->my_exchange;
1185 :
1186 0 : if (NULL != fo->at)
1187 : {
1188 0 : GNUNET_SCHEDULER_cancel (fo->at);
1189 0 : fo->at = NULL;
1190 : }
1191 0 : GNUNET_CONTAINER_DLL_remove (exchange->fo_head,
1192 : exchange->fo_tail,
1193 : fo);
1194 0 : GNUNET_free (fo->wire_method);
1195 0 : GNUNET_free (fo);
1196 0 : }
1197 :
1198 :
1199 : /**
1200 : * Function called on each configuration section. Finds sections
1201 : * about exchanges, parses the entries and tries to connect to
1202 : * it in order to fetch /keys.
1203 : *
1204 : * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
1205 : * @param section name of the section
1206 : */
1207 : static void
1208 0 : accept_exchanges (void *cls,
1209 : const char *section)
1210 : {
1211 0 : const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1212 : char *url;
1213 : char *mks;
1214 : struct Exchange *exchange;
1215 : char *currency;
1216 :
1217 0 : if (0 != strncasecmp (section,
1218 : "merchant-exchange-",
1219 : strlen ("merchant-exchange-")))
1220 0 : return;
1221 0 : if (GNUNET_OK !=
1222 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
1223 : section,
1224 : "CURRENCY",
1225 : ¤cy))
1226 : {
1227 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1228 : section,
1229 : "CURRENCY");
1230 0 : return;
1231 : }
1232 0 : if (0 != strcasecmp (currency,
1233 : TMH_currency))
1234 : {
1235 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1236 : "Exchange given in section `%s' is for another currency. Skipping.\n",
1237 : section);
1238 0 : GNUNET_free (currency);
1239 0 : return;
1240 : }
1241 0 : GNUNET_free (currency);
1242 0 : if (GNUNET_OK !=
1243 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
1244 : section,
1245 : "EXCHANGE_BASE_URL",
1246 : &url))
1247 : {
1248 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1249 : section,
1250 : "EXCHANGE_BASE_URL");
1251 0 : return;
1252 : }
1253 0 : exchange = GNUNET_new (struct Exchange);
1254 0 : exchange->url = url;
1255 0 : if (GNUNET_OK ==
1256 0 : GNUNET_CONFIGURATION_get_value_string (cfg,
1257 : section,
1258 : "MASTER_KEY",
1259 : &mks))
1260 : {
1261 0 : if (GNUNET_OK ==
1262 0 : GNUNET_CRYPTO_eddsa_public_key_from_string (mks,
1263 : strlen (mks),
1264 : &exchange->master_pub.
1265 : eddsa_pub))
1266 : {
1267 0 : exchange->trusted = true;
1268 : }
1269 : else
1270 : {
1271 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1272 : section,
1273 : "MASTER_KEY",
1274 : _ ("ill-formed EdDSA key"));
1275 : }
1276 0 : GNUNET_free (mks);
1277 : }
1278 : else
1279 : {
1280 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1281 : "MASTER_KEY missing in section '%s', not trusting exchange\n",
1282 : section);
1283 :
1284 : }
1285 0 : GNUNET_CONTAINER_DLL_insert (exchange_head,
1286 : exchange_tail,
1287 : exchange);
1288 0 : exchange->pending = true;
1289 0 : GNUNET_assert (NULL == exchange->retry_task);
1290 0 : exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
1291 : exchange);
1292 : }
1293 :
1294 :
1295 : enum GNUNET_GenericReturnValue
1296 0 : TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
1297 : {
1298 : merchant_curl_ctx
1299 0 : = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1300 : &merchant_curl_rc);
1301 0 : if (NULL == merchant_curl_ctx)
1302 : {
1303 0 : GNUNET_break (0);
1304 0 : return GNUNET_SYSERR;
1305 : }
1306 0 : merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx);
1307 0 : GNUNET_CURL_enable_async_scope_header (merchant_curl_ctx,
1308 : "Taler-Correlation-Id");
1309 : /* get exchanges from the merchant configuration and try to connect to them */
1310 0 : GNUNET_CONFIGURATION_iterate_sections (cfg,
1311 : &accept_exchanges,
1312 : (void *) cfg);
1313 : /* build JSON with list of trusted exchanges (will be included in contracts) */
1314 0 : TMH_trusted_exchanges = json_array ();
1315 0 : for (struct Exchange *exchange = exchange_head;
1316 : NULL != exchange;
1317 0 : exchange = exchange->next)
1318 : {
1319 : json_t *j_exchange;
1320 :
1321 0 : if (! exchange->trusted)
1322 0 : continue;
1323 0 : j_exchange = GNUNET_JSON_PACK (
1324 : GNUNET_JSON_pack_string ("url",
1325 : exchange->url),
1326 : GNUNET_JSON_pack_data_auto ("master_pub",
1327 : &exchange->master_pub));
1328 0 : GNUNET_assert (0 ==
1329 : json_array_append_new (TMH_trusted_exchanges,
1330 : j_exchange));
1331 : }
1332 0 : return json_array_size (TMH_trusted_exchanges);
1333 : }
1334 :
1335 :
1336 : void
1337 0 : TMH_EXCHANGES_done ()
1338 : {
1339 0 : while (NULL != exchange_head)
1340 0 : free_exchange_entry (exchange_head);
1341 0 : GNUNET_CURL_fini (merchant_curl_ctx);
1342 0 : merchant_curl_ctx = NULL;
1343 0 : GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
1344 0 : merchant_curl_rc = NULL;
1345 0 : json_decref (TMH_trusted_exchanges);
1346 0 : TMH_trusted_exchanges = NULL;
1347 0 : }
1348 :
1349 :
1350 : /* end of taler-merchant-httpd_exchanges.c */
|