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 3 : retry_exchange (void *cls)
303 : {
304 3 : struct Exchange *exchange = cls;
305 :
306 : /* might be a scheduled reload and not our first attempt */
307 3 : exchange->retry_task = NULL;
308 3 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
309 : "Connecting to exchange %s in retry_exchange()\n",
310 : exchange->url);
311 3 : if (NULL != exchange->conn)
312 : {
313 0 : TALER_EXCHANGE_disconnect (exchange->conn);
314 0 : exchange->conn = NULL;
315 : }
316 6 : exchange->conn = TALER_EXCHANGE_connect (merchant_curl_ctx,
317 3 : 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 3 : GNUNET_break (NULL != exchange->conn);
324 3 : }
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 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 : 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 : /* Do not warn if this is before our first attempt */
575 0 : if (! GNUNET_TIME_relative_is_zero (exchange->wire_retry_delay))
576 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
577 : "Exchange does not support `%s' wire method (will retry later)\n",
578 : fo->wire_method);
579 0 : fbw = NULL;
580 0 : continue;
581 : }
582 0 : if (NULL == fbw->af)
583 : {
584 : /* Disagreement on the current time */
585 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
586 : "Exchange has no wire fees configured for `%s' wire method (will retry later)\n",
587 : fo->wire_method);
588 0 : fbw = NULL;
589 0 : continue;
590 : }
591 0 : if (GNUNET_TIME_timestamp_cmp (fbw->af->start_date,
592 : >,
593 : now))
594 : {
595 : /* Disagreement on the current time */
596 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
597 : "Exchange's earliest fee is %s ahead of our time. Clock skew issue?\n",
598 : GNUNET_TIME_relative2s (
599 : GNUNET_TIME_absolute_get_remaining (
600 : fbw->af->start_date.abs_time),
601 : true));
602 0 : fbw = NULL;
603 0 : continue;
604 : }
605 : }
606 : else
607 : {
608 : /* no wire transfer method given, so we yield no fee */
609 0 : fbw = NULL;
610 : }
611 : {
612 0 : struct TALER_EXCHANGE_HttpResponse hr = {
613 : .http_status = MHD_HTTP_OK,
614 : };
615 :
616 0 : fo->fc (fo->fc_cls,
617 : &hr,
618 : exchange->conn,
619 : (NULL != fbw) ? fbw->payto_uri : NULL,
620 0 : (NULL != fbw) ? &fbw->af->fees.wire : NULL,
621 0 : exchange->trusted);
622 : }
623 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
624 : }
625 0 : return need_wire;
626 : }
627 :
628 :
629 : static void
630 : wire_task_cb (void *cls);
631 :
632 :
633 : /**
634 : * Callbacks of this type are used to serve the result of submitting a
635 : * wire format inquiry request to a exchange.
636 : *
637 : * If the request fails to generate a valid response from the
638 : * exchange, @a http_status will also be zero.
639 : *
640 : * Must only be called if 'exchange->pending' is #GNUNET_NO,
641 : * that is #TALER_EXCHANGE_get_keys() will succeed.
642 : *
643 : * @param cls closure, a `struct Exchange`
644 : * @param hr HTTP response details
645 : * @param accounts_len length of the @a accounts array
646 : * @param accounts list of wire accounts of the exchange, NULL on error
647 : */
648 : static void
649 0 : handle_wire_data (void *cls,
650 : const struct TALER_EXCHANGE_HttpResponse *hr,
651 : unsigned int accounts_len,
652 : const struct TALER_EXCHANGE_WireAccount *accounts)
653 : {
654 0 : struct Exchange *exchange = cls;
655 : const struct TALER_EXCHANGE_Keys *keys;
656 : enum TALER_ErrorCode ecx;
657 :
658 0 : exchange->wire_request = NULL;
659 0 : if (MHD_HTTP_OK != hr->http_status)
660 : {
661 : struct TMH_EXCHANGES_FindOperation *fo;
662 :
663 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
664 : "Failed to obtain /wire details from `%s': %u/%d\n",
665 : exchange->url,
666 : hr->http_status,
667 : hr->ec);
668 0 : while (NULL != (fo = exchange->fo_head))
669 : {
670 0 : fo->fc (fo->fc_cls,
671 : hr,
672 : exchange->conn,
673 : NULL,
674 : NULL,
675 : GNUNET_NO);
676 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
677 : }
678 0 : return;
679 : }
680 0 : keys = TALER_EXCHANGE_get_keys (exchange->conn);
681 0 : GNUNET_assert (NULL != keys);
682 0 : ecx = process_wire_accounts (exchange,
683 : &keys->master_pub,
684 : accounts_len,
685 : accounts);
686 0 : if (TALER_EC_NONE != ecx)
687 : {
688 : /* Report hard failure to all callbacks! */
689 : struct TMH_EXCHANGES_FindOperation *fo;
690 0 : struct TALER_EXCHANGE_HttpResponse hrx = {
691 : .ec = ecx,
692 : .http_status = 0,
693 0 : .reply = hr->reply
694 : };
695 :
696 0 : GNUNET_break_op (0);
697 0 : while (NULL != (fo = exchange->fo_head))
698 : {
699 0 : fo->fc (fo->fc_cls,
700 : &hrx,
701 : NULL,
702 : NULL,
703 : NULL,
704 : GNUNET_NO);
705 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
706 : }
707 0 : return;
708 : }
709 0 : if ( (process_find_operations (exchange)) &&
710 0 : (NULL == exchange->wire_task) &&
711 0 : (NULL == exchange->wire_request) )
712 : {
713 : /* need to run /wire again. But as we DID get a successful reply,
714 : and as the exchange is unlikely to offer new wire methods very
715 : frequently, start with some significant delay */
716 : exchange->wire_retry_delay
717 0 : = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_MINUTES,
718 : exchange->wire_retry_delay);
719 0 : exchange->wire_retry_delay = RETRY_BACKOFF (exchange->wire_retry_delay);
720 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
721 : "Exchange does not support our wire method. Retrying in %s\n",
722 :
723 : GNUNET_STRINGS_relative_time_to_string (
724 : exchange->wire_retry_delay,
725 : GNUNET_YES));
726 : exchange->wire_task
727 0 : = GNUNET_SCHEDULER_add_delayed (exchange->wire_retry_delay,
728 : &wire_task_cb,
729 : exchange);
730 : }
731 : }
732 :
733 :
734 : /**
735 : * Check if we have any remaining pending requests for the
736 : * given @a exchange, and if we have the required data, call
737 : * the callback. If requests without /wire data remain,
738 : * retry the /wire request after some delay.
739 : *
740 : * Must only be called if 'exchange->pending' is #GNUNET_NO,
741 : * that is #TALER_EXCHANGE_get_keys() will succeed.
742 : *
743 : * @param cls a `struct Exchange` to check
744 : */
745 : static void
746 0 : wire_task_cb (void *cls)
747 : {
748 0 : struct Exchange *exchange = cls;
749 :
750 0 : exchange->wire_task = NULL;
751 0 : GNUNET_assert (! exchange->pending);
752 0 : if (! process_find_operations (exchange))
753 0 : return; /* no more need */
754 0 : GNUNET_assert (NULL == exchange->wire_request);
755 0 : exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
756 : &handle_wire_data,
757 : exchange);
758 : }
759 :
760 :
761 : /**
762 : * Free @a exchange.
763 : *
764 : * @param[in] exchange entry to free
765 : */
766 : static void
767 3 : free_exchange_entry (struct Exchange *exchange)
768 : {
769 : struct FeesByWireMethod *f;
770 :
771 3 : GNUNET_CONTAINER_DLL_remove (exchange_head,
772 : exchange_tail,
773 : exchange);
774 3 : while (NULL != (f = exchange->wire_fees_head))
775 : {
776 : struct TALER_EXCHANGE_WireAggregateFees *af;
777 :
778 0 : GNUNET_CONTAINER_DLL_remove (exchange->wire_fees_head,
779 : exchange->wire_fees_tail,
780 : f);
781 0 : while (NULL != (af = f->af))
782 : {
783 0 : f->af = af->next;
784 0 : GNUNET_free (af);
785 : }
786 0 : GNUNET_free (f->wire_method);
787 0 : GNUNET_free (f->payto_uri);
788 0 : GNUNET_free (f);
789 : }
790 3 : if (NULL != exchange->wire_request)
791 : {
792 0 : TALER_EXCHANGE_wire_cancel (exchange->wire_request);
793 0 : exchange->wire_request = NULL;
794 : }
795 3 : if (NULL != exchange->wire_task)
796 : {
797 0 : GNUNET_SCHEDULER_cancel (exchange->wire_task);
798 0 : exchange->wire_task = NULL;
799 : }
800 3 : if (NULL != exchange->conn)
801 : {
802 3 : TALER_EXCHANGE_disconnect (exchange->conn);
803 3 : exchange->conn = NULL;
804 : }
805 3 : if (NULL != exchange->retry_task)
806 : {
807 0 : GNUNET_SCHEDULER_cancel (exchange->retry_task);
808 0 : exchange->retry_task = NULL;
809 : }
810 3 : GNUNET_assert (NULL == exchange->fo_head);
811 3 : GNUNET_assert (NULL == exchange->fo_tail);
812 3 : GNUNET_free (exchange->url);
813 3 : GNUNET_free (exchange);
814 3 : }
815 :
816 :
817 : /**
818 : * We failed downloading /keys from @a exchange. Tell clients
819 : * about our failure, abort pending operations and retry later.
820 : *
821 : * @param exchange exchange that failed
822 : * @param hr details about the HTTP reply
823 : * @param compat version compatibility data
824 : */
825 : static void
826 0 : fail_and_retry (struct Exchange *exchange,
827 : const struct TALER_EXCHANGE_HttpResponse *hr,
828 : enum TALER_EXCHANGE_VersionCompatibility compat)
829 : {
830 : struct TMH_EXCHANGES_FindOperation *fo;
831 :
832 0 : exchange->pending = true;
833 0 : if (NULL != exchange->wire_request)
834 : {
835 0 : TALER_EXCHANGE_wire_cancel (exchange->wire_request);
836 0 : exchange->wire_request = NULL;
837 : }
838 0 : if (NULL != exchange->wire_task)
839 : {
840 0 : GNUNET_SCHEDULER_cancel (exchange->wire_task);
841 0 : exchange->wire_task = NULL;
842 : }
843 0 : while (NULL != (fo = exchange->fo_head))
844 : {
845 0 : fo->fc (fo->fc_cls,
846 : hr,
847 : NULL,
848 : NULL,
849 : NULL,
850 : GNUNET_NO);
851 0 : TMH_EXCHANGES_find_exchange_cancel (fo);
852 : }
853 0 : if (TALER_EXCHANGE_VC_INCOMPATIBLE_NEWER == compat)
854 : {
855 : /* Log hard error: we likely need admin help! */
856 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
857 : "Exchange `%s' runs an incompatible more recent version of the Taler protocol. Will not retry. This client may need to be updated.\n",
858 : exchange->url);
859 : /* Theoretically, the exchange could downgrade,
860 : but let's not be too aggressive about retries
861 : on this one. */
862 0 : exchange->retry_delay = GNUNET_TIME_relative_max (GNUNET_TIME_UNIT_HOURS,
863 : exchange->retry_delay);
864 : }
865 0 : if ( (NULL == exchange->fo_head) &&
866 0 : (TALER_EC_GENERIC_CONFIGURATION_INVALID == hr->ec) )
867 : {
868 : /* This can NEVER work, so don't retry */
869 0 : free_exchange_entry (exchange);
870 0 : return;
871 : }
872 0 : exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
873 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
874 : "Failed to fetch /keys from `%s': %d/%u, retrying in %s\n",
875 : exchange->url,
876 : (int) hr->ec,
877 : hr->http_status,
878 : GNUNET_STRINGS_relative_time_to_string (exchange->retry_delay,
879 : GNUNET_YES));
880 0 : if (NULL != exchange->retry_task)
881 0 : GNUNET_SCHEDULER_cancel (exchange->retry_task);
882 0 : exchange->first_retry = GNUNET_TIME_relative_to_absolute (
883 : exchange->retry_delay);
884 0 : exchange->retry_task = GNUNET_SCHEDULER_add_delayed (exchange->retry_delay,
885 : &retry_exchange,
886 : exchange);
887 : }
888 :
889 :
890 : /**
891 : * Function called with information about who is auditing
892 : * a particular exchange and what key the exchange is using.
893 : *
894 : * @param cls closure, will be `struct Exchange` so that
895 : * when this function gets called, it will change the flag 'pending'
896 : * to 'false'. Note: 'keys' is automatically saved inside the exchange's
897 : * handle, which is contained inside 'struct Exchange', when
898 : * this callback is called. Thus, once 'pending' turns 'false',
899 : * it is safe to call 'TALER_EXCHANGE_get_keys()' on the exchange's handle,
900 : * in order to get the "good" keys.
901 : * @param hr http response details
902 : * @param keys information about the various keys used
903 : * by the exchange
904 : * @param compat version compatibility data
905 : */
906 : static void
907 0 : keys_mgmt_cb (void *cls,
908 : const struct TALER_EXCHANGE_HttpResponse *hr,
909 : const struct TALER_EXCHANGE_Keys *keys,
910 : enum TALER_EXCHANGE_VersionCompatibility compat)
911 : {
912 0 : struct Exchange *exchange = cls;
913 : struct GNUNET_TIME_Timestamp expire;
914 : struct GNUNET_TIME_Relative delay;
915 :
916 0 : if ( (MHD_HTTP_OK != hr->http_status) ||
917 : (NULL == keys) )
918 : {
919 0 : fail_and_retry (exchange,
920 : hr,
921 : compat);
922 0 : return;
923 : }
924 0 : if ( (exchange->trusted) &&
925 0 : (0 != GNUNET_memcmp (&exchange->master_pub,
926 : &keys->master_pub)) )
927 : {
928 : /* master pub differs => do not trust the exchange (without auditor) */
929 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
930 : "Master public key of exchange `%s' differs from our configuration. Not trusting exchange.\n",
931 : exchange->url);
932 0 : exchange->trusted = false;
933 : }
934 0 : if (! exchange->trusted)
935 : {
936 0 : exchange->master_pub = keys->master_pub;
937 0 : for (struct Exchange *e = exchange_head;
938 : NULL != e;
939 0 : e = e->next)
940 : {
941 0 : if (e == exchange)
942 0 : continue;
943 0 : if (! e->trusted)
944 0 : continue;
945 0 : if (0 ==
946 0 : GNUNET_memcmp (&e->master_pub,
947 : &exchange->master_pub))
948 0 : exchange->trusted = true; /* same exchange, different URL => trust applies */
949 : }
950 : }
951 0 : if (0 != (TALER_EXCHANGE_VC_NEWER & compat))
952 : {
953 : /* Warn user exactly once about need to upgrade */
954 : static int once;
955 :
956 0 : if (0 == once)
957 : {
958 0 : once = 1;
959 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
960 : "Exchange `%s' runs a more recent version of the Taler protocol. You may want to update this client.\n",
961 : exchange->url);
962 : }
963 : }
964 :
965 : /* store exchange online signing keys in our DB */
966 0 : for (unsigned int i = 0; i<keys->num_sign_keys; i++)
967 : {
968 0 : struct TALER_EXCHANGE_SigningPublicKey *sign_key = &keys->sign_keys[i];
969 : enum GNUNET_DB_QueryStatus qs;
970 :
971 0 : TMH_db->preflight (TMH_db->cls);
972 0 : qs = TMH_db->insert_exchange_signkey (TMH_db->cls,
973 : &keys->master_pub,
974 0 : &sign_key->key,
975 : sign_key->valid_from,
976 : sign_key->valid_until,
977 : sign_key->valid_legal,
978 0 : &sign_key->master_sig);
979 : /* 0 is OK, we may already have the key in the DB! */
980 0 : if (0 > qs)
981 : {
982 0 : GNUNET_break (0);
983 0 : fail_and_retry (exchange,
984 : hr,
985 : compat);
986 0 : return;
987 : }
988 : }
989 :
990 0 : exchange->first_retry = GNUNET_TIME_relative_to_absolute (RELOAD_DELAY);
991 0 : expire = TALER_EXCHANGE_check_keys_current (exchange->conn,
992 : TALER_EXCHANGE_CKF_NONE);
993 0 : if (0 == GNUNET_TIME_absolute_is_zero (expire.abs_time))
994 0 : delay = RELOAD_DELAY;
995 : else
996 0 : delay = GNUNET_TIME_absolute_get_remaining (expire.abs_time);
997 0 : if (GNUNET_TIME_relative_is_zero (delay))
998 : {
999 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1000 : "/keys response from exchange expired immediately! Retrying in 1 minute.\n");
1001 0 : delay = GNUNET_TIME_UNIT_MINUTES;
1002 : }
1003 0 : exchange->retry_delay = GNUNET_TIME_UNIT_ZERO;
1004 0 : if (NULL != exchange->retry_task)
1005 0 : GNUNET_SCHEDULER_cancel (exchange->retry_task);
1006 : exchange->retry_task
1007 0 : = GNUNET_SCHEDULER_add_delayed (delay,
1008 : &retry_exchange,
1009 : exchange);
1010 0 : exchange->pending = false;
1011 0 : if ( (process_find_operations (exchange)) &&
1012 0 : (NULL == exchange->wire_request) &&
1013 0 : (NULL == exchange->wire_task) )
1014 : {
1015 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1016 : "Got key data, but also need wire data. Will request /wire now\n");
1017 0 : exchange->wire_request = TALER_EXCHANGE_wire (exchange->conn,
1018 : &handle_wire_data,
1019 : exchange);
1020 : }
1021 : }
1022 :
1023 :
1024 : /**
1025 : * Task to return find operation result asynchronously to caller.
1026 : *
1027 : * @param cls a `struct TMH_EXCHANGES_FindOperation`
1028 : */
1029 : static void
1030 0 : return_result (void *cls)
1031 : {
1032 0 : struct TMH_EXCHANGES_FindOperation *fo = cls;
1033 0 : struct Exchange *exchange = fo->my_exchange;
1034 :
1035 0 : fo->at = NULL;
1036 0 : if ( (process_find_operations (exchange)) &&
1037 0 : (NULL == exchange->wire_request) &&
1038 0 : (! exchange->pending) &&
1039 0 : (NULL != exchange->wire_task) )
1040 : {
1041 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1042 : "Do not have current wire data. Will re-request /wire in 1 minute\n");
1043 : exchange->wire_task
1044 0 : = GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_MINUTES,
1045 : &wire_task_cb,
1046 : exchange);
1047 : }
1048 0 : }
1049 :
1050 :
1051 : struct TMH_EXCHANGES_FindOperation *
1052 0 : TMH_EXCHANGES_find_exchange (const char *chosen_exchange,
1053 : const char *wire_method,
1054 : int force_reload,
1055 : TMH_EXCHANGES_FindContinuation fc,
1056 : void *fc_cls)
1057 : {
1058 : struct Exchange *exchange;
1059 : struct TMH_EXCHANGES_FindOperation *fo;
1060 : struct GNUNET_TIME_Timestamp now;
1061 :
1062 0 : if (NULL == merchant_curl_ctx)
1063 : {
1064 0 : GNUNET_break (0);
1065 0 : return NULL;
1066 : }
1067 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1068 : "Trying to find chosen exchange `%s'\n",
1069 : chosen_exchange);
1070 : /* Check if the exchange is known */
1071 0 : for (exchange = exchange_head; NULL != exchange; exchange = exchange->next)
1072 : {
1073 : /* test it by checking URL */
1074 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1075 : "Comparing chosen exchange url '%s' with known url '%s'.\n",
1076 : chosen_exchange,
1077 : exchange->url);
1078 0 : if (0 == strcmp (exchange->url,
1079 : chosen_exchange))
1080 : {
1081 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1082 : "The exchange `%s' is already known (good)\n",
1083 : chosen_exchange);
1084 0 : break;
1085 : }
1086 : }
1087 0 : if (NULL == exchange)
1088 : {
1089 : /* This is a new exchange */
1090 0 : exchange = GNUNET_new (struct Exchange);
1091 0 : exchange->url = GNUNET_strdup (chosen_exchange);
1092 0 : exchange->pending = true;
1093 0 : GNUNET_CONTAINER_DLL_insert (exchange_head,
1094 : exchange_tail,
1095 : exchange);
1096 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1097 : "The exchange `%s' is new\n",
1098 : chosen_exchange);
1099 : }
1100 :
1101 0 : fo = GNUNET_new (struct TMH_EXCHANGES_FindOperation);
1102 0 : fo->fc = fc;
1103 0 : fo->fc_cls = fc_cls;
1104 0 : fo->my_exchange = exchange;
1105 0 : if (NULL != wire_method)
1106 0 : fo->wire_method = GNUNET_strdup (wire_method);
1107 0 : GNUNET_CONTAINER_DLL_insert (exchange->fo_head,
1108 : exchange->fo_tail,
1109 : fo);
1110 0 : if ( (force_reload) &&
1111 0 : (GNUNET_TIME_absolute_is_past (exchange->first_retry)) )
1112 : {
1113 : /* increment exponential-backoff */
1114 0 : exchange->retry_delay = RETRY_BACKOFF (exchange->retry_delay);
1115 : /* do not allow forced check until both backoff and #FORCED_RELOAD_DELAY
1116 : are satisfied again */
1117 : exchange->first_retry
1118 0 : = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_max (
1119 : exchange->retry_delay,
1120 : FORCED_RELOAD_DELAY));
1121 0 : TALER_EXCHANGE_check_keys_current (exchange->conn,
1122 : TALER_EXCHANGE_CKF_FORCE_DOWNLOAD);
1123 0 : return fo;
1124 : }
1125 :
1126 0 : now = GNUNET_TIME_timestamp_get ();
1127 0 : if ( (! exchange->pending) &&
1128 0 : ( (NULL == fo->wire_method) ||
1129 0 : (NULL != get_wire_fees (exchange,
1130 : now,
1131 0 : fo->wire_method)) ) )
1132 : {
1133 : /* We are not currently waiting for a reply, immediately
1134 : return result */
1135 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1136 : "The exchange `%s' is ready\n",
1137 : chosen_exchange);
1138 0 : GNUNET_assert (NULL == fo->at);
1139 0 : fo->at = GNUNET_SCHEDULER_add_now (&return_result,
1140 : fo);
1141 0 : return fo;
1142 : }
1143 :
1144 : /* If new or resumed, (re)try fetching /keys */
1145 0 : if ( (NULL == exchange->conn) &&
1146 0 : (NULL == exchange->retry_task) &&
1147 0 : (exchange->pending) )
1148 : {
1149 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1150 : "Do not have current /keys data for `%s'. Will request /keys now\n",
1151 : chosen_exchange);
1152 0 : exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
1153 : exchange);
1154 : }
1155 0 : else if ( (! exchange->pending) &&
1156 0 : (NULL == exchange->wire_task) &&
1157 0 : (NULL == exchange->wire_request) )
1158 : {
1159 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
1160 : "Do not have required wire data. Will re-request /wire now\n");
1161 0 : exchange->wire_task = GNUNET_SCHEDULER_add_now (&wire_task_cb,
1162 : exchange);
1163 : }
1164 0 : return fo;
1165 : }
1166 :
1167 :
1168 : void
1169 0 : TMH_EXCHANGES_find_exchange_cancel (struct TMH_EXCHANGES_FindOperation *fo)
1170 : {
1171 0 : struct Exchange *exchange = fo->my_exchange;
1172 :
1173 0 : if (NULL != fo->at)
1174 : {
1175 0 : GNUNET_SCHEDULER_cancel (fo->at);
1176 0 : fo->at = NULL;
1177 : }
1178 0 : GNUNET_CONTAINER_DLL_remove (exchange->fo_head,
1179 : exchange->fo_tail,
1180 : fo);
1181 0 : GNUNET_free (fo->wire_method);
1182 0 : GNUNET_free (fo);
1183 0 : }
1184 :
1185 :
1186 : /**
1187 : * Function called on each configuration section. Finds sections
1188 : * about exchanges, parses the entries and tries to connect to
1189 : * it in order to fetch /keys.
1190 : *
1191 : * @param cls closure, with a `const struct GNUNET_CONFIGURATION_Handle *`
1192 : * @param section name of the section
1193 : */
1194 : static void
1195 57 : accept_exchanges (void *cls,
1196 : const char *section)
1197 : {
1198 57 : const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
1199 : char *url;
1200 : char *mks;
1201 : struct Exchange *exchange;
1202 : char *currency;
1203 :
1204 57 : if (0 != strncasecmp (section,
1205 : "merchant-exchange-",
1206 : strlen ("merchant-exchange-")))
1207 54 : return;
1208 6 : if (GNUNET_OK !=
1209 6 : GNUNET_CONFIGURATION_get_value_string (cfg,
1210 : section,
1211 : "CURRENCY",
1212 : ¤cy))
1213 : {
1214 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1215 : section,
1216 : "CURRENCY");
1217 0 : return;
1218 : }
1219 6 : if (0 != strcasecmp (currency,
1220 : TMH_currency))
1221 : {
1222 3 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1223 : "Exchange given in section `%s' is for another currency. Skipping.\n",
1224 : section);
1225 3 : GNUNET_free (currency);
1226 3 : return;
1227 : }
1228 3 : GNUNET_free (currency);
1229 3 : if (GNUNET_OK !=
1230 3 : GNUNET_CONFIGURATION_get_value_string (cfg,
1231 : section,
1232 : "EXCHANGE_BASE_URL",
1233 : &url))
1234 : {
1235 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1236 : section,
1237 : "EXCHANGE_BASE_URL");
1238 0 : return;
1239 : }
1240 3 : exchange = GNUNET_new (struct Exchange);
1241 3 : exchange->url = url;
1242 3 : if (GNUNET_OK ==
1243 3 : GNUNET_CONFIGURATION_get_value_string (cfg,
1244 : section,
1245 : "MASTER_KEY",
1246 : &mks))
1247 : {
1248 3 : if (GNUNET_OK ==
1249 3 : GNUNET_CRYPTO_eddsa_public_key_from_string (mks,
1250 : strlen (mks),
1251 : &exchange->master_pub.
1252 : eddsa_pub))
1253 : {
1254 3 : exchange->trusted = true;
1255 : }
1256 : else
1257 : {
1258 0 : GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
1259 : section,
1260 : "MASTER_KEY",
1261 : _ ("ill-formed EdDSA key"));
1262 : }
1263 3 : GNUNET_free (mks);
1264 : }
1265 : else
1266 : {
1267 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1268 : "MASTER_KEY missing in section '%s', not trusting exchange\n",
1269 : section);
1270 :
1271 : }
1272 3 : GNUNET_CONTAINER_DLL_insert (exchange_head,
1273 : exchange_tail,
1274 : exchange);
1275 3 : exchange->pending = true;
1276 3 : GNUNET_assert (NULL == exchange->retry_task);
1277 3 : exchange->retry_task = GNUNET_SCHEDULER_add_now (&retry_exchange,
1278 : exchange);
1279 : }
1280 :
1281 :
1282 : enum GNUNET_GenericReturnValue
1283 3 : TMH_EXCHANGES_init (const struct GNUNET_CONFIGURATION_Handle *cfg)
1284 : {
1285 : merchant_curl_ctx
1286 3 : = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1287 : &merchant_curl_rc);
1288 3 : if (NULL == merchant_curl_ctx)
1289 : {
1290 0 : GNUNET_break (0);
1291 0 : return GNUNET_SYSERR;
1292 : }
1293 3 : merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (merchant_curl_ctx);
1294 3 : GNUNET_CURL_enable_async_scope_header (merchant_curl_ctx,
1295 : "Taler-Correlation-Id");
1296 : /* get exchanges from the merchant configuration and try to connect to them */
1297 3 : GNUNET_CONFIGURATION_iterate_sections (cfg,
1298 : &accept_exchanges,
1299 : (void *) cfg);
1300 : /* build JSON with list of trusted exchanges (will be included in contracts) */
1301 3 : TMH_trusted_exchanges = json_array ();
1302 6 : for (struct Exchange *exchange = exchange_head;
1303 : NULL != exchange;
1304 3 : exchange = exchange->next)
1305 : {
1306 : json_t *j_exchange;
1307 :
1308 3 : if (! exchange->trusted)
1309 0 : continue;
1310 3 : j_exchange = GNUNET_JSON_PACK (
1311 : GNUNET_JSON_pack_string ("url",
1312 : exchange->url),
1313 : GNUNET_JSON_pack_data_auto ("master_pub",
1314 : &exchange->master_pub));
1315 3 : GNUNET_assert (0 ==
1316 : json_array_append_new (TMH_trusted_exchanges,
1317 : j_exchange));
1318 : }
1319 3 : return json_array_size (TMH_trusted_exchanges);
1320 : }
1321 :
1322 :
1323 : void
1324 3 : TMH_EXCHANGES_done ()
1325 : {
1326 6 : while (NULL != exchange_head)
1327 3 : free_exchange_entry (exchange_head);
1328 3 : GNUNET_CURL_fini (merchant_curl_ctx);
1329 3 : merchant_curl_ctx = NULL;
1330 3 : GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
1331 3 : merchant_curl_rc = NULL;
1332 3 : json_decref (TMH_trusted_exchanges);
1333 3 : TMH_trusted_exchanges = NULL;
1334 3 : }
1335 :
1336 :
1337 : /* end of taler-merchant-httpd_exchanges.c */
|