Line data Source code
1 : /*
2 : This file is part of TALER
3 : Copyright (C) 2024 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 Affero General Public License for more details.
12 :
13 : You should have received a copy of the GNU Affero General Public License along with
14 : TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
15 : */
16 : /**
17 : * @file src/backend/taler-merchant-kyccheck.c
18 : * @brief Process that check the KYC status of our bank accounts at all exchanges
19 : * @author Christian Grothoff
20 : */
21 : #include "platform.h"
22 : struct Inquiry;
23 : #define TALER_EXCHANGE_GET_KYC_CHECK_RESULT_CLOSURE struct Inquiry
24 : #define TALER_EXCHANGE_GET_KYC_INFO_RESULT_CLOSURE struct Inquiry
25 : #define TALER_EXCHANGE_POST_KYC_UPLOAD_RESULT_CLOSURE struct Inquiry
26 : #include "microhttpd.h"
27 : #include <gnunet/gnunet_util_lib.h>
28 : #include <jansson.h>
29 : #include <pthread.h>
30 : #include <regex.h>
31 : #include <taler/taler_dbevents.h>
32 : #include <taler/taler_json_lib.h>
33 : #include <taler/taler_exchange_service.h>
34 : #include <taler/exchange/post-kyc-upload-ID.h>
35 : #include "taler/taler_merchant_util.h"
36 : #include "taler/taler_merchant_bank_lib.h"
37 : #include "merchantdb_lib.h"
38 : #include "merchantdb_lib.h"
39 : #include "merchant-database/iterate_outdated_kyc_statuses.h"
40 : #include "merchant-database/insert_kyc_status.h"
41 : #include "merchant-database/delete_tos_accepted_early.h"
42 : #include "merchant-database/get_kyc_status.h"
43 : #include "merchant-database/get_tos_accepted_early.h"
44 : #include "merchant-database/set_instance.h"
45 : #include "merchant-database/iterate_accounts.h"
46 : #include "merchant-database/get_exchange_keys.h"
47 : #include "merchant-database/event_listen.h"
48 : #include "merchant-database/preflight.h"
49 : #include "merchant-database/start.h"
50 :
51 :
52 : /**
53 : * Timeout for the exchange interaction. Rather long as we should do
54 : * long-polling and do not want to wake up too often.
55 : */
56 : #define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
57 : GNUNET_TIME_UNIT_MINUTES, \
58 : 30)
59 :
60 : /**
61 : * How long do we wait between requests if all we wait
62 : * for is a change in the AML investigation status?
63 : * Default value.
64 : */
65 : #define AML_FREQ GNUNET_TIME_relative_multiply ( \
66 : GNUNET_TIME_UNIT_HOURS, \
67 : 6)
68 :
69 : /**
70 : * How long do we wait between requests if all we wait
71 : * for is a change in the AML investigation status?
72 : */
73 : static struct GNUNET_TIME_Relative aml_freq;
74 :
75 : /**
76 : * How frequently do we check for updates to our KYC status
77 : * if there is no actual reason to check? Set to a very low
78 : * frequency, just to ensure we eventually notice.
79 : * Default value.
80 : */
81 : #define AML_LOW_FREQ GNUNET_TIME_relative_multiply ( \
82 : GNUNET_TIME_UNIT_DAYS, \
83 : 7)
84 :
85 : /**
86 : * How frequently do we check for updates to our KYC status
87 : * if there is no actual reason to check? Set to a very low
88 : * frequency, just to ensure we eventually notice.
89 : */
90 : static struct GNUNET_TIME_Relative aml_low_freq;
91 :
92 :
93 : /**
94 : * How many inquiries do we process concurrently at most.
95 : */
96 : #define OPEN_INQUIRY_LIMIT 1024
97 :
98 : /**
99 : * Name of the KYC form (``FORM_ID``) the exchange uses to affirm
100 : * acceptance of the terms of service. Must match the value submitted
101 : * by #TALER_EXCHANGE_post_kyc_upload_accept_tos_create().
102 : */
103 : #define ACCEPT_TOS_FORM "accept-tos"
104 :
105 : /**
106 : * Minimum delay before we retry after the exchange returned an
107 : * internal error to our attempt to automatically accept the terms
108 : * of service on behalf of the user.
109 : */
110 : #define TOS_ERROR_RETRY_DELAY GNUNET_TIME_UNIT_HOURS
111 :
112 :
113 : /**
114 : * Information about an exchange.
115 : */
116 : struct Exchange
117 : {
118 : /**
119 : * Kept in a DLL.
120 : */
121 : struct Exchange *next;
122 :
123 : /**
124 : * Kept in a DLL.
125 : */
126 : struct Exchange *prev;
127 :
128 : /**
129 : * The keys of this exchange
130 : */
131 : struct TALER_EXCHANGE_Keys *keys;
132 :
133 : };
134 :
135 :
136 : /**
137 : * Information about an Account.
138 : */
139 : struct Account
140 : {
141 : /**
142 : * Kept in a DLL.
143 : */
144 : struct Account *next;
145 :
146 : /**
147 : * Kept in a DLL.
148 : */
149 : struct Account *prev;
150 :
151 : /**
152 : * Head of inquiries for this account.
153 : */
154 : struct Inquiry *i_head;
155 :
156 : /**
157 : * Tail of inquiries for this account.
158 : */
159 : struct Inquiry *i_tail;
160 :
161 : /**
162 : * Merchant instance this account belongs to.
163 : */
164 : char *instance_id;
165 :
166 : /**
167 : * The payto-URI of this account.
168 : */
169 : struct TALER_FullPayto merchant_account_uri;
170 :
171 : /**
172 : * Wire hash of the merchant bank account (with the
173 : * respective salt).
174 : */
175 : struct TALER_MerchantWireHashP h_wire;
176 :
177 : /**
178 : * Private key of the instance.
179 : */
180 : union TALER_AccountPrivateKeyP ap;
181 :
182 : /**
183 : * Hash of the @e merchant_account_uri.
184 : */
185 : struct TALER_NormalizedPaytoHashP h_payto;
186 :
187 : /**
188 : * Database generation when this account
189 : * was last active.
190 : */
191 : uint64_t account_gen;
192 :
193 : };
194 :
195 :
196 : /**
197 : * Information about an inquiry job.
198 : */
199 : struct Inquiry
200 : {
201 : /**
202 : * Key in the index of inquiries by instance, account and exchange.
203 : */
204 : struct GNUNET_HashCode key;
205 :
206 : /**
207 : * Kept in a DLL.
208 : */
209 : struct Inquiry *next;
210 :
211 : /**
212 : * Kept in a DLL.
213 : */
214 : struct Inquiry *prev;
215 :
216 : /**
217 : * Main task for this inquiry.
218 : */
219 : struct GNUNET_SCHEDULER_Task *task;
220 :
221 : /**
222 : * Which exchange is this inquiry about.
223 : */
224 : struct Exchange *e;
225 :
226 : /**
227 : * Which account is this inquiry about.
228 : */
229 : struct Account *a;
230 :
231 : /**
232 : * AccountLimits that apply to the account, NULL
233 : * if unknown.
234 : */
235 : json_t *jlimits;
236 :
237 : /**
238 : * Handle for the actual HTTP request to the exchange.
239 : */
240 : struct TALER_EXCHANGE_GetKycCheckHandle *kyc;
241 :
242 : /**
243 : * Handle for fetching /kyc-info to discover the upload ID used to
244 : * automatically accept the terms of service, NULL if not active.
245 : */
246 : struct TALER_EXCHANGE_GetKycInfoHandle *kyc_info;
247 :
248 : /**
249 : * Handle for the /kyc-upload request submitting the automatic
250 : * terms-of-service acceptance, NULL if not active.
251 : */
252 : struct TALER_EXCHANGE_PostKycUploadHandle *tos_upload;
253 :
254 : /**
255 : * If non-NULL, the ``Taler-Terms-Version`` of the terms of service
256 : * that the user accepted early (via ``POST /private/accept-tos-early``)
257 : * and that we are trying to submit to the exchange on their behalf.
258 : * Owned by the inquiry.
259 : */
260 : char *tos_etag;
261 :
262 : /**
263 : * Access token for the /kyc-info API.
264 : */
265 : struct TALER_AccountAccessTokenP access_token;
266 :
267 : /**
268 : * Last time we called the /kyc-check endpoint.
269 : */
270 : struct GNUNET_TIME_Timestamp last_kyc_check;
271 :
272 : /**
273 : * When is the next KYC check due?
274 : */
275 : struct GNUNET_TIME_Absolute due;
276 :
277 : /**
278 : * When should the current KYC time out?
279 : */
280 : struct GNUNET_TIME_Absolute timeout;
281 :
282 : /**
283 : * Current exponential backoff.
284 : */
285 : struct GNUNET_TIME_Relative backoff;
286 :
287 : /**
288 : * Rule generation known to the client, 0 for none.
289 : * Corresponds to the decision row in the exchange.
290 : */
291 : uint64_t rule_gen;
292 :
293 : /**
294 : * Last HTTP status returned by the exchange from
295 : * the /kyc-check endpoint.
296 : */
297 : unsigned int last_http_status;
298 :
299 : /**
300 : * Last Taler error code returned by the exchange from
301 : * the /kyc-check endpoint.
302 : */
303 : enum TALER_ErrorCode last_ec;
304 :
305 : /**
306 : * True if this is not our first time we make this request.
307 : */
308 : bool not_first_time;
309 :
310 : /**
311 : * Do soft limits on transactions apply to this merchant for operations
312 : * merchants care about? If so, we should increase our request frequency
313 : * and ask more often to see if they were lifted.
314 : */
315 : bool zero_limited;
316 :
317 : /**
318 : * Did we not run this inquiry due to limits?
319 : */
320 : bool limited;
321 :
322 : /**
323 : * Do we believe this account's KYC is in good shape?
324 : */
325 : bool kyc_ok;
326 :
327 : /**
328 : * True if merchant did perform this account's KYC AUTH transfer and @e access_token is set.
329 : */
330 : bool auth_ok;
331 :
332 : /**
333 : * True if the account is known to be currently under
334 : * investigation by AML staff.
335 : */
336 : bool aml_review;
337 :
338 : };
339 :
340 :
341 : /**
342 : * Head of known exchanges.
343 : */
344 : static struct Exchange *e_head;
345 :
346 : /**
347 : * Tail of known exchanges.
348 : */
349 : static struct Exchange *e_tail;
350 :
351 : /**
352 : * Head of accounts.
353 : */
354 : static struct Account *a_head;
355 :
356 : /**
357 : * Tail of accounts.
358 : */
359 : static struct Account *a_tail;
360 :
361 : /**
362 : * The merchant's configuration.
363 : */
364 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
365 :
366 : /**
367 : * Our database connection.
368 : */
369 : static struct TALER_MERCHANTDB_PostgresContext *pg;
370 :
371 : /**
372 : * Handle to the context for interacting with the bank.
373 : */
374 : static struct GNUNET_CURL_Context *ctx;
375 :
376 : /**
377 : * Scheduler context for running the @e ctx.
378 : */
379 : static struct GNUNET_CURL_RescheduleContext *rc;
380 :
381 : /**
382 : * Event handler to learn that there may be new bank
383 : * accounts to check.
384 : */
385 : static struct GNUNET_DB_EventHandler *eh_accounts;
386 :
387 : /**
388 : * Event handler to learn that there may be new exchange
389 : * keys to check.
390 : */
391 : static struct GNUNET_DB_EventHandler *eh_keys;
392 :
393 : /**
394 : * Event handler to learn that there was a KYC
395 : * rule triggered and we need to check the KYC
396 : * status for an account.
397 : */
398 : static struct GNUNET_DB_EventHandler *eh_rule;
399 :
400 : /**
401 : * Event handler to learn that higher-frequency KYC
402 : * checks were forced by an application actively inspecting
403 : * some KYC status values.
404 : */
405 : static struct GNUNET_DB_EventHandler *eh_update_forced;
406 :
407 : /**
408 : * Event handler to learn that we got new /keys
409 : * from an exchange and should reconsider eligibility.
410 : */
411 : static struct GNUNET_DB_EventHandler *keys_rule;
412 :
413 : /**
414 : * Main task to discover (new) accounts.
415 : */
416 : static struct GNUNET_SCHEDULER_Task *account_task;
417 :
418 : /**
419 : * Pending refreshes, coalesced by instance serial.
420 : */
421 : struct Refresh
422 : {
423 : struct Refresh *next;
424 : struct Refresh *prev;
425 : struct GNUNET_HashCode key;
426 : uint64_t merchant_serial;
427 : };
428 :
429 : static struct Refresh *refresh_head;
430 : static struct Refresh *refresh_tail;
431 : static struct GNUNET_CONTAINER_MultiHashMap *refresh_map;
432 : static struct GNUNET_CONTAINER_MultiHashMap *inquiry_map;
433 : static struct GNUNET_SCHEDULER_Task *refresh_task;
434 :
435 : /**
436 : * Counter determining how often we have called
437 : * "iterate_accounts" on the database.
438 : */
439 : static uint64_t database_gen;
440 :
441 : /**
442 : * How many active inquiries do we have right now.
443 : */
444 : static unsigned int active_inquiries;
445 :
446 : /**
447 : * Value to return from main(). 0 on success, non-zero on errors.
448 : */
449 : static int global_ret;
450 :
451 : /**
452 : * Should we enable HTTP/2 and HTTP/3 when talking to the exchange?
453 : * Those are not expected to be terribly beneficial for a client with
454 : * stable connections to a few servers, but they could cause stability
455 : * issues with libcurl. Hence we *default* to HTTP/1.1-only, as that
456 : * is the conservative and most tested code path.
457 : */
458 : static int enable_h3;
459 :
460 : /**
461 : * #GNUNET_YES if we are in test mode and should exit when idle.
462 : */
463 : static int test_mode;
464 :
465 : /**
466 : * True if the last DB query was limited by the
467 : * #OPEN_INQUIRY_LIMIT and we thus should check again
468 : * as soon as we are substantially below that limit,
469 : * and not only when we get a DB notification.
470 : */
471 : static bool at_limit;
472 :
473 :
474 : /**
475 : * Check about performing a /kyc-check request with the
476 : * exchange for the given inquiry.
477 : *
478 : * @param cls a `struct Inquiry` to process
479 : */
480 : static void
481 : inquiry_work (void *cls);
482 :
483 :
484 : /**
485 : * Hash a fully qualified inquiry identity. Include string terminators to
486 : * keep adjacent components unambiguous.
487 : */
488 : static void
489 38 : inquiry_key (const char *instance_id,
490 : const struct TALER_MerchantWireHashP *h_wire,
491 : const char *exchange_url,
492 : struct GNUNET_HashCode *key)
493 : {
494 38 : struct GNUNET_HashContext *hc = GNUNET_CRYPTO_hash_context_start ();
495 :
496 38 : GNUNET_CRYPTO_hash_context_read (hc,
497 : instance_id,
498 38 : strlen (instance_id) + 1);
499 38 : GNUNET_CRYPTO_hash_context_read (hc,
500 : h_wire,
501 : sizeof (*h_wire));
502 38 : GNUNET_CRYPTO_hash_context_read (hc,
503 : exchange_url,
504 38 : strlen (exchange_url) + 1);
505 38 : GNUNET_CRYPTO_hash_context_finish (hc,
506 : key);
507 38 : }
508 :
509 :
510 : /**
511 : * An inquiry keeps its active slot through automatic ToS acceptance.
512 : */
513 : static bool
514 17 : inquiry_busy (const struct Inquiry *i)
515 : {
516 28 : return (NULL != i->kyc) ||
517 27 : (NULL != i->kyc_info) ||
518 10 : (NULL != i->tos_upload);
519 : }
520 :
521 :
522 : /**
523 : * Request an immediate check, sharing any active or queued work.
524 : */
525 : static void
526 17 : request_inquiry (struct Inquiry *i)
527 : {
528 17 : if (inquiry_busy (i))
529 9 : return;
530 9 : i->due = GNUNET_TIME_UNIT_ZERO_ABS;
531 9 : if (i->limited)
532 1 : return;
533 8 : if (NULL != i->task)
534 7 : GNUNET_SCHEDULER_cancel (i->task);
535 8 : i->task = GNUNET_SCHEDULER_add_now (&inquiry_work,
536 : i);
537 : }
538 :
539 :
540 : /**
541 : * An inquiry finished, check if we should resume others.
542 : */
543 : static void
544 36 : end_inquiry (void)
545 : {
546 36 : GNUNET_assert (active_inquiries > 0);
547 36 : active_inquiries--;
548 36 : if ( (active_inquiries < OPEN_INQUIRY_LIMIT / 2) &&
549 : (at_limit) )
550 : {
551 1 : at_limit = false;
552 1 : for (struct Account *a = a_head;
553 2 : NULL != a;
554 1 : a = a->next)
555 : {
556 1 : for (struct Inquiry *i = a->i_head;
557 2 : NULL != i;
558 1 : i = i->next)
559 : {
560 1 : if (! i->limited)
561 0 : continue;
562 1 : i->limited = false;
563 1 : GNUNET_assert (NULL == i->task);
564 : /* done synchronously so that the active_inquiries
565 : is updated immediately */
566 1 : inquiry_work (i);
567 1 : if (at_limit)
568 0 : break;
569 : }
570 1 : if (at_limit)
571 0 : break;
572 : }
573 : }
574 36 : if ( (! at_limit) &&
575 36 : (0 == active_inquiries) &&
576 : (test_mode) )
577 : {
578 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
579 : "No more open inquiries and in test mode. Existing.\n");
580 1 : GNUNET_SCHEDULER_shutdown ();
581 1 : return;
582 : }
583 : }
584 :
585 :
586 : /**
587 : * Pack the given @a limit into the JSON @a limits array.
588 : *
589 : * @param limit account limit to pack
590 : * @param[in,out] limits JSON array to extend
591 : */
592 : static void
593 5 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
594 : json_t *limits)
595 : {
596 : json_t *jl;
597 :
598 5 : jl = GNUNET_JSON_PACK (
599 : TALER_JSON_pack_kycte ("operation_type",
600 : limit->operation_type),
601 : GNUNET_JSON_pack_time_rel ("timeframe",
602 : limit->timeframe),
603 : TALER_JSON_pack_amount ("threshold",
604 : &limit->threshold),
605 : GNUNET_JSON_pack_bool ("soft_limit",
606 : limit->soft_limit)
607 : );
608 5 : GNUNET_assert (0 ==
609 : json_array_append_new (limits,
610 : jl));
611 5 : }
612 :
613 :
614 : /**
615 : * Update KYC status for @a i based on
616 : * @a account_kyc_status
617 : *
618 : * @param[in,out] i inquiry context, jlimits is updated
619 : * @param account_kyc_status account KYC status details
620 : */
621 : static void
622 7 : store_kyc_status (
623 : struct Inquiry *i,
624 : const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
625 : {
626 : json_t *jlimits;
627 :
628 7 : json_decref (i->jlimits);
629 7 : jlimits = json_array ();
630 7 : GNUNET_assert (NULL != jlimits);
631 7 : i->zero_limited = false;
632 12 : for (unsigned int j = 0; j<account_kyc_status->limits_length; j++)
633 : {
634 5 : const struct TALER_EXCHANGE_AccountLimit *limit
635 5 : = &account_kyc_status->limits[j];
636 :
637 5 : pack_limit (limit,
638 : jlimits);
639 5 : if (TALER_amount_is_zero (&limit->threshold) &&
640 1 : limit->soft_limit &&
641 1 : ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT == limit->operation_type) ||
642 1 : (TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE == limit->operation_type) ||
643 0 : (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION == limit->operation_type) ) )
644 : {
645 1 : i->zero_limited = true;
646 : }
647 : }
648 7 : i->jlimits = jlimits;
649 7 : GNUNET_break (! GNUNET_is_zero (&account_kyc_status->access_token));
650 7 : i->access_token = account_kyc_status->access_token;
651 7 : i->aml_review = account_kyc_status->aml_review;
652 7 : i->kyc_ok = (MHD_HTTP_OK == i->last_http_status);
653 7 : }
654 :
655 :
656 : /**
657 : * The current interaction with the exchange for inquiry @a i is
658 : * complete (or was aborted). Schedule the next periodic KYC check
659 : * at @a i->due and release the active-inquiry slot.
660 : *
661 : * @param[in,out] i the inquiry to reschedule
662 : */
663 : static void
664 35 : finish_inquiry (struct Inquiry *i)
665 : {
666 35 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
667 : "Will repeat inquiry in %s\n",
668 : GNUNET_TIME_relative2s (
669 : GNUNET_TIME_absolute_get_remaining (i->due),
670 : true));
671 35 : if (! GNUNET_TIME_absolute_is_never (i->due))
672 35 : i->task = GNUNET_SCHEDULER_add_at (i->due,
673 : &inquiry_work,
674 : i);
675 35 : end_inquiry ();
676 35 : }
677 :
678 :
679 : /**
680 : * Clear the tos-accepted data from the user, we do not
681 : * need the flag anymore, either because we passed it on
682 : * to the exchange or because they are too old.
683 : *
684 : * @param i inquiry this is about
685 : */
686 : static void
687 0 : clear_tos (const struct Inquiry *i)
688 : {
689 : enum GNUNET_DB_QueryStatus qs;
690 :
691 0 : qs = TALER_MERCHANTDB_set_instance (pg,
692 0 : i->a->instance_id);
693 0 : if (qs < 0)
694 : {
695 0 : GNUNET_break (0);
696 0 : global_ret = EXIT_FAILURE;
697 0 : GNUNET_SCHEDULER_shutdown ();
698 0 : return;
699 : }
700 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
701 : {
702 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
703 : "Instance `%s' vanished, nothing to clear\n",
704 : i->a->instance_id);
705 0 : return;
706 : }
707 0 : qs = TALER_MERCHANTDB_delete_tos_accepted_early (
708 : pg,
709 0 : i->a->instance_id,
710 0 : i->e->keys->exchange_url);
711 0 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
712 : TALER_MERCHANTDB_set_instance (pg,
713 : NULL));
714 0 : if (qs < 0)
715 : {
716 0 : GNUNET_break (0);
717 0 : global_ret = EXIT_FAILURE;
718 0 : GNUNET_SCHEDULER_shutdown ();
719 0 : return;
720 : }
721 : }
722 :
723 :
724 : /**
725 : * Function called with the result of submitting an automatic
726 : * terms-of-service acceptance to the exchange via /kyc-upload.
727 : *
728 : * @param i the inquiry the acceptance was for
729 : * @param pr the exchange's response
730 : */
731 : static void
732 0 : tos_upload_cb (struct Inquiry *i,
733 : const struct TALER_EXCHANGE_PostKycUploadResponse *pr)
734 : {
735 0 : unsigned int http_status = pr->hr.http_status;
736 :
737 0 : i->tos_upload = NULL;
738 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
739 : "Automatic ToS acceptance for `%s' at `%s' returned HTTP %u\n",
740 : i->a->merchant_account_uri.full_payto,
741 : i->e->keys->exchange_url,
742 : http_status);
743 0 : switch (http_status)
744 : {
745 0 : case MHD_HTTP_OK:
746 : case MHD_HTTP_NO_CONTENT:
747 : /* Exchange accepted the terms of service: re-check KYC now. */
748 0 : i->due = GNUNET_TIME_UNIT_ZERO_ABS;
749 0 : clear_tos (i);
750 0 : break;
751 0 : case 0: /* no answer, like network failure */
752 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
753 : case MHD_HTTP_BAD_GATEWAY:
754 : case MHD_HTTP_REQUEST_ENTITY_TOO_LARGE: /* Wild error */
755 : /* Internal/transient error at the exchange: do NOT clear the early
756 : acceptance, but back off for at least an hour before retrying
757 : with a regular periodic KYC check. */
758 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
759 : "Exchange `%s' failed to process automatic ToS acceptance (HTTP %u); retrying later\n",
760 : i->e->keys->exchange_url,
761 : http_status);
762 0 : i->due = GNUNET_TIME_relative_to_absolute (
763 : GNUNET_TIME_randomize (TOS_ERROR_RETRY_DELAY));
764 0 : break;
765 0 : case MHD_HTTP_NOT_FOUND:
766 : /* Something must have changed exchange-side, try again
767 : immediately, but do not clear ToS acceptance */
768 0 : i->due = GNUNET_TIME_UNIT_ZERO_ABS;
769 0 : clear_tos (i);
770 0 : break;
771 0 : case MHD_HTTP_BAD_REQUEST:
772 : /* This should not happen, go back to manual KYC */
773 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
774 : "Exchange `%s' failed to process automatic ToS acceptance (HTTP %u); retrying later\n",
775 : i->e->keys->exchange_url,
776 : http_status);
777 0 : i->due = GNUNET_TIME_relative_to_absolute (
778 : GNUNET_TIME_randomize (TOS_ERROR_RETRY_DELAY));
779 0 : break;
780 0 : case MHD_HTTP_CONFLICT:
781 : /* Exchange rejected the accepted ToS version (ETag not acceptable
782 : or ToS acceptance disappeared):
783 : clear the early acceptance so we do not loop, then re-check KYC
784 : (the user will have to accept the ToS through the regular flow
785 : if it still applies). */
786 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
787 : "Exchange `%s' rejected early ToS acceptance (version `%s', HTTP %u); clearing early acceptance\n",
788 : i->e->keys->exchange_url,
789 : i->tos_etag,
790 : http_status);
791 0 : clear_tos (i);
792 0 : i->due = GNUNET_TIME_UNIT_ZERO_ABS;
793 0 : break;
794 : }
795 0 : GNUNET_free (i->tos_etag);
796 0 : finish_inquiry (i);
797 0 : }
798 :
799 :
800 : /**
801 : * Submit an automatic terms-of-service acceptance for inquiry @a i to
802 : * the exchange, using the @a id of the corresponding KYC requirement
803 : * (obtained from /kyc-info) and the early-accepted version in
804 : * @a i->tos_etag.
805 : *
806 : * @param[in,out] i inquiry to submit the ToS acceptance for
807 : * @param id KYC requirement / upload ID for the terms-of-service form
808 : */
809 : static void
810 0 : start_tos_upload (struct Inquiry *i,
811 : const char *id)
812 : {
813 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
814 : "Submitting automatic ToS acceptance (version `%s', id `%s') to `%s'\n",
815 : i->tos_etag,
816 : id,
817 : i->e->keys->exchange_url);
818 0 : i->tos_upload = TALER_EXCHANGE_post_kyc_upload_accept_tos_create (
819 : ctx,
820 0 : i->e->keys->exchange_url,
821 : id,
822 0 : i->tos_etag);
823 0 : if ( (NULL == i->tos_upload) ||
824 : (TALER_EC_NONE !=
825 0 : TALER_EXCHANGE_post_kyc_upload_start (i->tos_upload,
826 : &tos_upload_cb,
827 : i)) )
828 : {
829 0 : GNUNET_break (0);
830 0 : if (NULL != i->tos_upload)
831 : {
832 0 : TALER_EXCHANGE_post_kyc_upload_cancel (i->tos_upload);
833 0 : i->tos_upload = NULL;
834 : }
835 : /* Could not even start the upload: treat as transient, keep the
836 : early acceptance and retry with a regular periodic check. */
837 0 : GNUNET_free (i->tos_etag);
838 0 : finish_inquiry (i);
839 : }
840 0 : }
841 :
842 :
843 : /**
844 : * Function called with the result of fetching /kyc-info while trying
845 : * to automatically accept the terms of service. Finds the ID of the
846 : * terms-of-service requirement and submits the acceptance.
847 : *
848 : * @param i the inquiry the lookup was for
849 : * @param ir the exchange's response
850 : */
851 : static void
852 0 : tos_info_cb (struct Inquiry *i,
853 : const struct TALER_EXCHANGE_GetKycInfoResponse *ir)
854 : {
855 0 : i->kyc_info = NULL;
856 0 : if (MHD_HTTP_OK == ir->hr.http_status)
857 : {
858 0 : const char *id = NULL;
859 :
860 0 : for (size_t j = 0; j < ir->details.ok.requirements_length; j++)
861 : {
862 0 : const struct TALER_EXCHANGE_RequirementInformation *req
863 0 : = &ir->details.ok.requirements[j];
864 :
865 0 : if ( (NULL != req->form) &&
866 0 : (NULL != req->id) &&
867 0 : (0 == strcmp (req->form,
868 : ACCEPT_TOS_FORM)) )
869 : {
870 0 : id = req->id;
871 0 : break;
872 : }
873 : }
874 0 : if (NULL != id)
875 : {
876 0 : start_tos_upload (i,
877 : id);
878 0 : return;
879 : }
880 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
881 : "No `%s' requirement at `%s'; cannot auto-accept ToS, falling back to periodic check\n",
882 : ACCEPT_TOS_FORM,
883 : i->e->keys->exchange_url);
884 : }
885 : else
886 : {
887 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
888 : "GET /kyc-info at `%s' returned HTTP %u; cannot auto-accept ToS now\n",
889 : i->e->keys->exchange_url,
890 : ir->hr.http_status);
891 : }
892 : /* Could not determine the upload ID: keep the early acceptance and
893 : retry on the next regular periodic KYC check. */
894 0 : GNUNET_free (i->tos_etag);
895 0 : finish_inquiry (i);
896 : }
897 :
898 :
899 : /**
900 : * Start the automatic terms-of-service acceptance for inquiry @a i by
901 : * fetching /kyc-info to discover the ID of the terms-of-service
902 : * requirement. The early-accepted version is in @a i->tos_etag.
903 : *
904 : * @param[in,out] i inquiry to auto-accept the terms of service for
905 : */
906 : static void
907 0 : start_tos_info (struct Inquiry *i)
908 : {
909 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
910 : "Fetching /kyc-info from `%s' to auto-accept ToS for `%s'\n",
911 : i->e->keys->exchange_url,
912 : i->a->merchant_account_uri.full_payto);
913 0 : i->kyc_info = TALER_EXCHANGE_get_kyc_info_create (ctx,
914 0 : i->e->keys->exchange_url,
915 0 : &i->access_token);
916 0 : if ( (NULL == i->kyc_info) ||
917 : (TALER_EC_NONE !=
918 0 : TALER_EXCHANGE_get_kyc_info_start (i->kyc_info,
919 : &tos_info_cb,
920 : i)) )
921 : {
922 0 : GNUNET_break (0);
923 0 : if (NULL != i->kyc_info)
924 : {
925 0 : TALER_EXCHANGE_get_kyc_info_cancel (i->kyc_info);
926 0 : i->kyc_info = NULL;
927 : }
928 : /* Could not start the lookup: keep the early acceptance and retry
929 : with a regular periodic check. */
930 0 : GNUNET_free (i->tos_etag);
931 0 : finish_inquiry (i);
932 : }
933 0 : }
934 :
935 :
936 : /**
937 : * The exchange asked us (via @a tos_required) to accept its terms of
938 : * service. Check whether the user already accepted the terms of
939 : * service early (via ``POST /private/accept-tos-early``). If so,
940 : * remember the accepted version in @a i->tos_etag so that we will try
941 : * to submit it to the exchange automatically.
942 : *
943 : * @param[in,out] i inquiry for which the exchange requires ToS acceptance
944 : * @param req required ETag for the accepted ToS
945 : */
946 : static void
947 0 : check_early_tos_acceptance (struct Inquiry *i,
948 : const char *req)
949 : {
950 : enum GNUNET_DB_QueryStatus qs;
951 0 : char *tos_version = NULL;
952 :
953 0 : qs = TALER_MERCHANTDB_set_instance (pg,
954 0 : i->a->instance_id);
955 0 : if (qs < 0)
956 : {
957 0 : GNUNET_break (0);
958 0 : global_ret = EXIT_FAILURE;
959 0 : GNUNET_SCHEDULER_shutdown ();
960 0 : return;
961 : }
962 0 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
963 : {
964 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
965 : "Instance `%s' vanished, skipping ToS check\n",
966 : i->a->instance_id);
967 0 : return;
968 : }
969 0 : qs = TALER_MERCHANTDB_get_tos_accepted_early (pg,
970 0 : i->a->instance_id,
971 0 : i->e->keys->exchange_url,
972 : &tos_version);
973 0 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
974 : TALER_MERCHANTDB_set_instance (pg,
975 : NULL));
976 0 : if (qs < 0)
977 : {
978 0 : GNUNET_break (0);
979 0 : global_ret = EXIT_FAILURE;
980 0 : GNUNET_SCHEDULER_shutdown ();
981 0 : return;
982 : }
983 0 : if (NULL == tos_version)
984 : {
985 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
986 : "Exchange `%s' supports early ToS acceptance, but user did not accept ToS early\n",
987 : i->e->keys->exchange_url);
988 0 : return;
989 : }
990 0 : if (0 != strcmp (tos_version,
991 : req))
992 : {
993 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
994 : "User accepted outdated ToS version `%s' early, but exchange wants `%s'. User will need to accept the ToS again!\n",
995 : tos_version,
996 : req);
997 0 : GNUNET_free (tos_version);
998 0 : return;
999 : }
1000 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1001 : "User accepted ToS version `%s' early; will submit to `%s'\n",
1002 : tos_version,
1003 : i->e->keys->exchange_url);
1004 0 : GNUNET_free (i->tos_etag);
1005 0 : i->tos_etag = tos_version;
1006 : }
1007 :
1008 :
1009 : /**
1010 : * Function called with the result of a KYC check.
1011 : *
1012 : * @param cls a `struct Inquiry *`
1013 : * @param ks the account's KYC status details
1014 : */
1015 : static void
1016 35 : exchange_check_cb (
1017 : struct Inquiry *i,
1018 : const struct TALER_EXCHANGE_GetKycCheckResponse *ks)
1019 : {
1020 35 : bool progress = false;
1021 :
1022 35 : i->kyc = NULL;
1023 35 : if (! i->not_first_time)
1024 23 : progress = true;
1025 35 : if ( (i->last_http_status != ks->hr.http_status) &&
1026 26 : (0 != ks->hr.http_status) )
1027 26 : progress = true;
1028 35 : if (0 != ks->hr.http_status)
1029 : {
1030 35 : i->last_http_status = ks->hr.http_status;
1031 35 : i->last_ec = ks->hr.ec;
1032 : }
1033 35 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1034 : "KYC status of `%s' / %s at `%s' is %u\n",
1035 : i->a->merchant_account_uri.full_payto,
1036 : i->a->instance_id,
1037 : i->e->keys->exchange_url,
1038 : ks->hr.http_status);
1039 35 : switch (ks->hr.http_status)
1040 : {
1041 0 : case 0:
1042 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1043 : "Exchange did not responded to /kyc-check request!\n");
1044 : i->backoff
1045 0 : = GNUNET_TIME_randomized_backoff (i->backoff,
1046 : EXCHANGE_TIMEOUT);
1047 0 : i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
1048 0 : break;
1049 6 : case MHD_HTTP_OK:
1050 6 : if (i->rule_gen != ks->details.ok.rule_gen)
1051 3 : progress = true;
1052 6 : i->rule_gen = ks->details.ok.rule_gen;
1053 6 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
1054 : /* exchange says KYC is OK, gives status information */
1055 6 : i->auth_ok = true;
1056 6 : store_kyc_status (i,
1057 : &ks->details.ok);
1058 6 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
1059 6 : if (i->aml_review || i->zero_limited)
1060 : {
1061 5 : if (! progress)
1062 2 : i->due = GNUNET_TIME_relative_to_absolute (
1063 : GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_freq,
1064 : i->backoff)));
1065 : }
1066 : else
1067 : {
1068 : /* KYC is OK, only check again if triggered */
1069 1 : if (! progress)
1070 0 : i->due = GNUNET_TIME_relative_to_absolute (
1071 : GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq,
1072 : i->backoff)));
1073 : }
1074 6 : break;
1075 1 : case MHD_HTTP_ACCEPTED:
1076 1 : if (i->rule_gen != ks->details.accepted.rule_gen)
1077 0 : progress = true;
1078 1 : i->rule_gen = ks->details.accepted.rule_gen;
1079 1 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
1080 : /* exchange says KYC is required */
1081 1 : i->auth_ok = true;
1082 1 : store_kyc_status (i,
1083 : &ks->details.accepted);
1084 1 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
1085 : /* Start immediately with long-polling */
1086 1 : if (! progress)
1087 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
1088 : i->timeout);
1089 1 : if (NULL != ks->details.accepted.tos_required)
1090 : {
1091 : /* Exchange wants the user to accept its terms of service.
1092 : If the user already accepted them early, try to submit that
1093 : acceptance to the exchange automatically. */
1094 0 : check_early_tos_acceptance (i,
1095 0 : ks->details.accepted.tos_required);
1096 : }
1097 1 : break;
1098 27 : case MHD_HTTP_NO_CONTENT:
1099 27 : i->rule_gen = 0;
1100 27 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
1101 27 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
1102 : /* exchange claims KYC is off! */
1103 27 : i->kyc_ok = true;
1104 27 : i->aml_review = false;
1105 : /* Clear limits, in case exchange had KYC on previously */
1106 27 : json_decref (i->jlimits);
1107 27 : i->jlimits = NULL;
1108 : /* KYC is OK, only check again if triggered */
1109 27 : i->due = GNUNET_TIME_relative_to_absolute (
1110 : GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq,
1111 : i->backoff)));
1112 27 : break;
1113 0 : case MHD_HTTP_FORBIDDEN: /* bad signature */
1114 0 : i->rule_gen = 0;
1115 0 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
1116 : /* Forbidden => KYC auth must be wrong */
1117 0 : i->auth_ok = false;
1118 : /* Start with long-polling */
1119 0 : if (! progress)
1120 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
1121 : i->timeout);
1122 0 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
1123 0 : break;
1124 1 : case MHD_HTTP_NOT_FOUND: /* account unknown */
1125 1 : i->rule_gen = 0;
1126 1 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
1127 : /* Account unknown => no KYC auth yet */
1128 1 : i->auth_ok = false;
1129 : /* unknown account => wire transfer required! */
1130 1 : i->kyc_ok = false;
1131 : /* There should not be any limits yet, but clear them
1132 : just in case the exchange has amnesia */
1133 1 : json_decref (i->jlimits);
1134 1 : i->jlimits = NULL;
1135 : /* Start immediately with Long-polling */
1136 1 : if (! progress)
1137 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
1138 : i->timeout);
1139 1 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
1140 1 : break;
1141 0 : case MHD_HTTP_CONFLICT: /* no account_pub known */
1142 0 : i->rule_gen = 0;
1143 0 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
1144 : /* Conflict => KYC auth wire transfer missing! */
1145 0 : i->auth_ok = false;
1146 : /* Start immediately with Long-polling */
1147 0 : if (! progress)
1148 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
1149 : i->timeout);
1150 0 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
1151 0 : break;
1152 0 : default:
1153 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1154 : "Exchange responded with HTTP status %u (%d) to /kyc-check request!\n",
1155 : ks->hr.http_status,
1156 : ks->hr.ec);
1157 : i->backoff
1158 0 : = GNUNET_TIME_randomized_backoff (i->backoff,
1159 : EXCHANGE_TIMEOUT);
1160 0 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
1161 0 : i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
1162 0 : i->auth_ok = false;
1163 0 : break;
1164 : }
1165 :
1166 : {
1167 : enum GNUNET_DB_QueryStatus qs;
1168 :
1169 35 : qs = TALER_MERCHANTDB_set_instance (pg,
1170 35 : i->a->instance_id);
1171 35 : if (qs < 0)
1172 : {
1173 0 : GNUNET_break (0);
1174 0 : global_ret = EXIT_FAILURE;
1175 0 : GNUNET_SCHEDULER_shutdown ();
1176 0 : return;
1177 : }
1178 35 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1179 : {
1180 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1181 : "Instance `%s' vanished, discarding KYC status\n",
1182 : i->a->instance_id);
1183 0 : finish_inquiry (i);
1184 0 : return;
1185 : }
1186 35 : qs = TALER_MERCHANTDB_insert_kyc_status (
1187 : pg,
1188 35 : i->a->instance_id,
1189 35 : &i->a->h_wire,
1190 35 : i->e->keys->exchange_url,
1191 : i->last_kyc_check,
1192 : i->due,
1193 : i->backoff,
1194 : i->last_http_status,
1195 : i->last_ec,
1196 : i->rule_gen,
1197 35 : (i->auth_ok)
1198 : ? &i->access_token
1199 : : NULL,
1200 35 : i->jlimits,
1201 35 : i->aml_review,
1202 35 : i->kyc_ok);
1203 35 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
1204 : TALER_MERCHANTDB_set_instance (
1205 : pg,
1206 : NULL));
1207 35 : if (qs < 0)
1208 : {
1209 0 : GNUNET_break (0);
1210 0 : global_ret = EXIT_FAILURE;
1211 0 : GNUNET_SCHEDULER_shutdown ();
1212 0 : return;
1213 : }
1214 35 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1215 : "insert_kyc_status (%s, %s, %u, %s, %s) returned %d\n",
1216 : i->a->instance_id,
1217 : i->e->keys->exchange_url,
1218 : i->last_http_status,
1219 : i->auth_ok ? "auth OK" : "auth needed",
1220 : NULL == i->jlimits ? "default limits" : "custom limits",
1221 : (int) qs);
1222 35 : i->not_first_time = true;
1223 : }
1224 35 : if (NULL != i->tos_etag)
1225 : {
1226 : /* The user accepted the terms of service early and the exchange now
1227 : requires acceptance: try to submit it automatically (this keeps
1228 : the active-inquiry slot) instead of waiting for the next check. */
1229 0 : start_tos_info (i);
1230 0 : return;
1231 : }
1232 35 : finish_inquiry (i);
1233 : }
1234 :
1235 :
1236 : static void
1237 37 : inquiry_work (void *cls)
1238 : {
1239 37 : struct Inquiry *i = cls;
1240 : enum TALER_EXCHANGE_KycLongPollTarget lpt;
1241 :
1242 37 : i->task = NULL;
1243 37 : if (! GNUNET_TIME_absolute_is_past (i->due))
1244 : {
1245 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1246 : "Will start inquiry on %s for %s in %s\n",
1247 : i->a->merchant_account_uri.full_payto,
1248 : i->e->keys->exchange_url,
1249 : GNUNET_TIME_relative2s (
1250 : GNUNET_TIME_absolute_get_remaining (i->due),
1251 : true));
1252 : i->task
1253 0 : = GNUNET_SCHEDULER_add_at (i->due,
1254 : &inquiry_work,
1255 : i);
1256 0 : goto finish;
1257 : }
1258 :
1259 37 : GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
1260 37 : if (OPEN_INQUIRY_LIMIT <= active_inquiries)
1261 : {
1262 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1263 : "Not looking for work: at limit\n");
1264 1 : i->limited = true;
1265 1 : at_limit = true;
1266 1 : return;
1267 : }
1268 36 : at_limit = false;
1269 : i->timeout
1270 36 : = GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT);
1271 36 : lpt = TALER_EXCHANGE_KLPT_NONE;
1272 36 : if (! i->auth_ok)
1273 30 : lpt = TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER;
1274 6 : else if (! i->kyc_ok)
1275 1 : lpt = TALER_EXCHANGE_KLPT_KYC_OK;
1276 5 : else if (i->aml_review)
1277 3 : lpt = TALER_EXCHANGE_KLPT_INVESTIGATION_DONE;
1278 36 : if (! i->not_first_time)
1279 24 : lpt = TALER_EXCHANGE_KLPT_NONE; /* no long polling on 1st call */
1280 36 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1281 : "Starting KYC status of `%s' for %s at `%s' (%d, %d, %d) using LPT %d\n",
1282 : i->a->merchant_account_uri.full_payto,
1283 : i->a->instance_id,
1284 : i->e->keys->exchange_url,
1285 : i->not_first_time,
1286 : i->auth_ok,
1287 : i->kyc_ok,
1288 : lpt);
1289 72 : i->kyc = TALER_EXCHANGE_get_kyc_check_create (
1290 : ctx,
1291 36 : i->e->keys->exchange_url,
1292 36 : &i->a->h_payto,
1293 36 : &i->a->ap);
1294 36 : if (NULL == i->kyc)
1295 : {
1296 0 : GNUNET_break (0);
1297 0 : i->due = i->timeout;
1298 : i->task
1299 0 : = GNUNET_SCHEDULER_add_at (i->due,
1300 : &inquiry_work,
1301 : i);
1302 0 : goto finish;
1303 : }
1304 36 : GNUNET_assert (GNUNET_OK ==
1305 : TALER_EXCHANGE_get_kyc_check_set_options (
1306 : i->kyc,
1307 : TALER_EXCHANGE_get_kyc_check_option_known_rule_gen (
1308 : i->rule_gen),
1309 : TALER_EXCHANGE_get_kyc_check_option_lpt (lpt),
1310 : TALER_EXCHANGE_get_kyc_check_option_timeout (
1311 : i->not_first_time && (! test_mode)
1312 : ? EXCHANGE_TIMEOUT
1313 : : GNUNET_TIME_UNIT_ZERO)));
1314 36 : GNUNET_assert (TALER_EC_NONE ==
1315 : TALER_EXCHANGE_get_kyc_check_start (i->kyc,
1316 : &exchange_check_cb,
1317 : i));
1318 36 : active_inquiries++;
1319 36 : finish:
1320 36 : if ( (0 == active_inquiries) &&
1321 : (test_mode) )
1322 : {
1323 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1324 : "No more open inquiries and in test mode. Existing.\n");
1325 0 : GNUNET_SCHEDULER_shutdown ();
1326 0 : return;
1327 : }
1328 : }
1329 :
1330 :
1331 : /**
1332 : * Check if the account @a could work with exchange that
1333 : * has keys @a keys.
1334 : *
1335 : * @param keys the keys of an exchange
1336 : * @param a an account
1337 : */
1338 : static bool
1339 24 : is_eligible (const struct TALER_EXCHANGE_Keys *keys,
1340 : const struct Account *a)
1341 : {
1342 : struct TALER_NormalizedPayto np;
1343 : bool ret;
1344 :
1345 24 : np = TALER_payto_normalize (a->merchant_account_uri);
1346 24 : ret = TALER_EXCHANGE_keys_test_account_allowed (keys,
1347 : true,
1348 : np);
1349 24 : GNUNET_free (np.normalized_payto);
1350 24 : return ret;
1351 : }
1352 :
1353 :
1354 : /**
1355 : * Start the KYC checking for account @a at exchange @a e.
1356 : *
1357 : * @param e an exchange
1358 : * @param a an account
1359 : */
1360 : static void
1361 23 : start_inquiry (struct Exchange *e,
1362 : struct Account *a)
1363 : {
1364 : struct Inquiry *i;
1365 : enum GNUNET_DB_QueryStatus qs;
1366 :
1367 23 : i = GNUNET_new (struct Inquiry);
1368 23 : i->e = e;
1369 23 : i->a = a;
1370 23 : inquiry_key (a->instance_id,
1371 23 : &a->h_wire,
1372 23 : e->keys->exchange_url,
1373 : &i->key);
1374 23 : GNUNET_assert (GNUNET_OK ==
1375 : GNUNET_CONTAINER_multihashmap_put (
1376 : inquiry_map,
1377 : &i->key,
1378 : i,
1379 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
1380 23 : GNUNET_CONTAINER_DLL_insert (a->i_head,
1381 : a->i_tail,
1382 : i);
1383 23 : qs = TALER_MERCHANTDB_set_instance (pg,
1384 23 : a->instance_id);
1385 23 : if (qs < 0)
1386 : {
1387 0 : GNUNET_break (0);
1388 0 : global_ret = EXIT_FAILURE;
1389 0 : GNUNET_SCHEDULER_shutdown ();
1390 0 : return;
1391 : }
1392 23 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1393 : {
1394 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1395 : "Instance `%s' vanished, not starting inquiry\n",
1396 : a->instance_id);
1397 0 : return;
1398 : }
1399 23 : qs = TALER_MERCHANTDB_get_kyc_status (pg,
1400 : a->merchant_account_uri,
1401 23 : a->instance_id,
1402 23 : e->keys->exchange_url,
1403 : &i->auth_ok,
1404 : &i->access_token,
1405 : &i->kyc_ok,
1406 : &i->last_http_status,
1407 : &i->last_ec,
1408 : &i->rule_gen,
1409 : &i->last_kyc_check,
1410 : &i->due,
1411 : &i->backoff,
1412 : &i->aml_review,
1413 : &i->jlimits);
1414 23 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1415 : "iterate_kyc_statuses (%s, %s, %s) returned %d (%u, #%llu)\n",
1416 : i->a->instance_id,
1417 : e->keys->exchange_url,
1418 : a->merchant_account_uri.full_payto,
1419 : (int) qs,
1420 : i->last_http_status,
1421 : (unsigned long long) i->rule_gen);
1422 23 : if (qs < 0)
1423 : {
1424 0 : GNUNET_break (0);
1425 0 : global_ret = EXIT_FAILURE;
1426 0 : GNUNET_SCHEDULER_shutdown ();
1427 0 : return;
1428 : }
1429 23 : if (qs > 0)
1430 0 : i->not_first_time = true;
1431 23 : if (TALER_EC_MERCHANT_PRIVATE_ACCOUNT_NOT_ELIGIBLE_FOR_EXCHANGE == i->last_ec)
1432 : {
1433 : /* Eligibility was restored. Do not retain the ineligible status's
1434 : infinite delay or backoff. */
1435 0 : i->due = GNUNET_TIME_UNIT_ZERO_ABS;
1436 0 : i->backoff = GNUNET_TIME_UNIT_ZERO;
1437 0 : i->not_first_time = false;
1438 : }
1439 23 : if (GNUNET_YES == test_mode)
1440 1 : i->due = GNUNET_TIME_UNIT_ZERO_ABS; /* immediately */
1441 23 : inquiry_work (i);
1442 : }
1443 :
1444 :
1445 : /**
1446 : * Stop KYC inquiry @a i.
1447 : *
1448 : * @param[in] i the inquiry to stop
1449 : */
1450 : static void
1451 24 : stop_inquiry (struct Inquiry *i)
1452 : {
1453 24 : struct Account *a = i->a;
1454 :
1455 24 : GNUNET_assert (GNUNET_YES ==
1456 : GNUNET_CONTAINER_multihashmap_remove (inquiry_map,
1457 : &i->key,
1458 : i));
1459 24 : GNUNET_CONTAINER_DLL_remove (a->i_head,
1460 : a->i_tail,
1461 : i);
1462 24 : if (NULL != i->task)
1463 : {
1464 23 : GNUNET_SCHEDULER_cancel (i->task);
1465 23 : i->task = NULL;
1466 : }
1467 24 : if (NULL != i->kyc)
1468 : {
1469 1 : TALER_EXCHANGE_get_kyc_check_cancel (i->kyc);
1470 1 : i->kyc = NULL;
1471 : }
1472 24 : if (NULL != i->kyc_info)
1473 : {
1474 0 : TALER_EXCHANGE_get_kyc_info_cancel (i->kyc_info);
1475 0 : i->kyc_info = NULL;
1476 : }
1477 24 : if (NULL != i->tos_upload)
1478 : {
1479 0 : TALER_EXCHANGE_post_kyc_upload_cancel (i->tos_upload);
1480 0 : i->tos_upload = NULL;
1481 : }
1482 24 : GNUNET_free (i->tos_etag);
1483 24 : if (NULL != i->jlimits)
1484 : {
1485 1 : json_decref (i->jlimits);
1486 1 : i->jlimits = NULL;
1487 : }
1488 24 : GNUNET_free (i);
1489 24 : }
1490 :
1491 :
1492 : /**
1493 : * Stop KYC inquiry for account @a at exchange @a e.
1494 : *
1495 : * @param e an exchange
1496 : * @param a an account
1497 : */
1498 : static void
1499 0 : stop_inquiry_at (struct Exchange *e,
1500 : struct Account *a)
1501 : {
1502 0 : for (struct Inquiry *i = a->i_head;
1503 0 : NULL != i;
1504 0 : i = i->next)
1505 : {
1506 0 : if (e == i->e)
1507 : {
1508 0 : stop_inquiry (i);
1509 0 : return;
1510 : }
1511 : }
1512 : /* strange, there should have been a match! */
1513 0 : GNUNET_break (0);
1514 : }
1515 :
1516 :
1517 : /**
1518 : * Set the account @a h_wire of @a instance_id to be ineligible
1519 : * for the exchange at @a exchange_url and thus no need to do KYC checks.
1520 : *
1521 : * @param instance_id instance that has the account
1522 : * @param exchange_url base URL of the exchange
1523 : * @param h_wire hash of the merchant bank account that is ineligible
1524 : */
1525 : static void
1526 1 : flag_ineligible (const char *instance_id,
1527 : const char *exchange_url,
1528 : const struct TALER_MerchantWireHashP *h_wire)
1529 : {
1530 : enum GNUNET_DB_QueryStatus qs;
1531 :
1532 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1533 : "Account %s not eligible at exchange %s\n",
1534 : TALER_B2S (h_wire),
1535 : exchange_url);
1536 1 : qs = TALER_MERCHANTDB_set_instance (pg,
1537 : instance_id);
1538 1 : if (qs < 0)
1539 : {
1540 0 : GNUNET_break (0);
1541 0 : global_ret = EXIT_FAILURE;
1542 0 : GNUNET_SCHEDULER_shutdown ();
1543 0 : return;
1544 : }
1545 1 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1546 : {
1547 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1548 : "Instance `%s' vanished, not flagging account\n",
1549 : instance_id);
1550 0 : return;
1551 : }
1552 1 : qs = TALER_MERCHANTDB_insert_kyc_status (
1553 : pg,
1554 : instance_id,
1555 : h_wire,
1556 : exchange_url,
1557 : GNUNET_TIME_timestamp_get (),
1558 1 : GNUNET_TIME_UNIT_FOREVER_ABS,
1559 1 : GNUNET_TIME_UNIT_FOREVER_REL,
1560 : 0,
1561 : TALER_EC_MERCHANT_PRIVATE_ACCOUNT_NOT_ELIGIBLE_FOR_EXCHANGE,
1562 : 0,
1563 : NULL,
1564 : NULL,
1565 : false,
1566 : false);
1567 1 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
1568 : TALER_MERCHANTDB_set_instance (
1569 : pg,
1570 : NULL));
1571 1 : if (qs < 0)
1572 : {
1573 0 : GNUNET_break (0);
1574 0 : global_ret = EXIT_FAILURE;
1575 0 : GNUNET_SCHEDULER_shutdown ();
1576 0 : return;
1577 : }
1578 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1579 : "insert_kyc_status (%s) returned %d\n",
1580 : exchange_url,
1581 : (int) qs);
1582 : }
1583 :
1584 :
1585 : /**
1586 : * Start inquries for all exchanges on account @a a.
1587 : *
1588 : * @param a an account
1589 : */
1590 : static void
1591 28 : start_inquiries (struct Account *a)
1592 : {
1593 28 : for (struct Exchange *e = e_head;
1594 52 : NULL != e;
1595 24 : e = e->next)
1596 : {
1597 24 : if (is_eligible (e->keys,
1598 : a))
1599 : {
1600 23 : start_inquiry (e,
1601 : a);
1602 : }
1603 : else
1604 : {
1605 1 : flag_ineligible (a->instance_id,
1606 1 : e->keys->exchange_url,
1607 1 : &a->h_wire);
1608 : }
1609 : }
1610 28 : }
1611 :
1612 :
1613 : /**
1614 : * Stop all inquries involving account @a a.
1615 : *
1616 : * @param a an account
1617 : */
1618 : static void
1619 59 : stop_inquiries (struct Account *a)
1620 : {
1621 : struct Inquiry *i;
1622 :
1623 82 : while (NULL != (i = a->i_head))
1624 23 : stop_inquiry (i);
1625 59 : }
1626 :
1627 :
1628 : /**
1629 : * Callback invoked with information about a bank account.
1630 : *
1631 : * @param cls closure
1632 : * @param merchant_priv private key of the merchant instance
1633 : * @param ad details about the account
1634 : */
1635 : static void
1636 50 : account_cb (
1637 : void *cls,
1638 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
1639 : const struct TALER_MERCHANTDB_AccountDetails *ad)
1640 : {
1641 50 : struct TALER_FullPayto payto_uri = ad->payto_uri;
1642 :
1643 50 : if (! ad->active)
1644 22 : return;
1645 49 : if (NULL == merchant_priv)
1646 0 : return; /* instance was deleted */
1647 49 : for (struct Account *a = a_head;
1648 74 : NULL != a;
1649 25 : a = a->next)
1650 : {
1651 46 : if ( (0 ==
1652 46 : TALER_full_payto_cmp (payto_uri,
1653 35 : a->merchant_account_uri)) &&
1654 : (0 ==
1655 35 : GNUNET_memcmp (&a->h_wire,
1656 21 : &ad->h_wire)) &&
1657 : (0 ==
1658 21 : strcmp (ad->instance_id,
1659 21 : a->instance_id)) )
1660 : {
1661 21 : a->account_gen = database_gen;
1662 21 : return;
1663 : }
1664 : }
1665 : {
1666 28 : struct Account *a = GNUNET_new (struct Account);
1667 :
1668 28 : a->account_gen = database_gen;
1669 : a->merchant_account_uri.full_payto
1670 28 : = GNUNET_strdup (ad->payto_uri.full_payto);
1671 : a->instance_id
1672 28 : = GNUNET_strdup (ad->instance_id);
1673 : a->h_wire
1674 28 : = ad->h_wire;
1675 : a->ap.merchant_priv
1676 28 : = *merchant_priv;
1677 28 : TALER_full_payto_normalize_and_hash (a->merchant_account_uri,
1678 : &a->h_payto);
1679 28 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1680 : "Found account %s of instance %s with H_PAYTO %s\n",
1681 : ad->payto_uri.full_payto,
1682 : ad->instance_id,
1683 : GNUNET_sh2s (&a->h_payto.hash));
1684 28 : GNUNET_CONTAINER_DLL_insert (a_head,
1685 : a_tail,
1686 : a);
1687 28 : start_inquiries (a);
1688 : }
1689 : }
1690 :
1691 :
1692 : /**
1693 : * The set of bank accounts has changed, update our
1694 : * list of active inquiries.
1695 : *
1696 : * @param cls unused
1697 : */
1698 : static void
1699 64 : find_accounts (void *cls)
1700 : {
1701 : enum GNUNET_DB_QueryStatus qs;
1702 :
1703 : (void) cls;
1704 64 : account_task = NULL;
1705 64 : database_gen++;
1706 64 : qs = TALER_MERCHANTDB_iterate_accounts (pg,
1707 : &account_cb,
1708 : NULL);
1709 64 : if (qs < 0)
1710 : {
1711 0 : GNUNET_break (0);
1712 0 : global_ret = EXIT_FAILURE;
1713 0 : GNUNET_SCHEDULER_shutdown ();
1714 0 : return;
1715 : }
1716 64 : for (struct Account *a = a_head;
1717 144 : NULL != a;
1718 80 : a = a->next)
1719 : {
1720 80 : if (a->account_gen < database_gen)
1721 31 : stop_inquiries (a);
1722 : }
1723 64 : if ( (! at_limit) &&
1724 64 : (0 == active_inquiries) &&
1725 : (test_mode) )
1726 : {
1727 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1728 : "No more open inquiries and in test mode. Existing.\n");
1729 0 : GNUNET_SCHEDULER_shutdown ();
1730 0 : return;
1731 : }
1732 : }
1733 :
1734 :
1735 : /**
1736 : * Function called when transfers are added to the merchant database. We look
1737 : * for more work.
1738 : *
1739 : * @param cls closure (NULL)
1740 : * @param extra additional event data provided
1741 : * @param extra_size number of bytes in @a extra
1742 : */
1743 : static void
1744 46 : account_changed (void *cls,
1745 : const void *extra,
1746 : size_t extra_size)
1747 : {
1748 : (void) cls;
1749 : (void) extra;
1750 : (void) extra_size;
1751 46 : if (NULL != account_task)
1752 1 : return;
1753 45 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1754 : "Received account change notification: reloading accounts\n");
1755 : account_task
1756 45 : = GNUNET_SCHEDULER_add_now (&find_accounts,
1757 : NULL);
1758 : }
1759 :
1760 :
1761 : /**
1762 : * Interact with the database to get the current set
1763 : * of exchange keys known to us.
1764 : *
1765 : * @param exchange_url the exchange URL to check
1766 : */
1767 : static void
1768 32 : find_keys (const char *exchange_url)
1769 : {
1770 : enum GNUNET_DB_QueryStatus qs;
1771 : struct TALER_EXCHANGE_Keys *keys;
1772 : struct Exchange *e;
1773 : struct GNUNET_TIME_Absolute first_retry;
1774 :
1775 32 : qs = TALER_MERCHANTDB_get_exchange_keys (pg,
1776 : exchange_url,
1777 : &first_retry,
1778 : &keys);
1779 32 : if (qs < 0)
1780 : {
1781 0 : GNUNET_break (0);
1782 0 : global_ret = EXIT_FAILURE;
1783 0 : GNUNET_SCHEDULER_shutdown ();
1784 18 : return;
1785 : }
1786 32 : if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) ||
1787 17 : (NULL == keys) )
1788 : {
1789 18 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1790 : "No %s/keys yet!\n",
1791 : exchange_url);
1792 18 : return;
1793 : }
1794 14 : for (e = e_head; NULL != e; e = e->next)
1795 : {
1796 0 : if (0 == strcmp (e->keys->exchange_url,
1797 0 : keys->exchange_url))
1798 : {
1799 0 : struct TALER_EXCHANGE_Keys *old_keys = e->keys;
1800 :
1801 0 : e->keys = keys;
1802 0 : for (struct Account *a = a_head;
1803 0 : NULL != a;
1804 0 : a = a->next)
1805 : {
1806 : bool was_eligible;
1807 : bool now_eligible;
1808 :
1809 0 : if (a->account_gen != database_gen)
1810 0 : continue;
1811 0 : was_eligible = is_eligible (old_keys,
1812 : a);
1813 0 : now_eligible = is_eligible (keys,
1814 : a);
1815 :
1816 0 : if (was_eligible == now_eligible)
1817 0 : continue; /* no change, do nothing */
1818 0 : if (was_eligible)
1819 : {
1820 0 : flag_ineligible (a->instance_id,
1821 0 : keys->exchange_url,
1822 0 : &a->h_wire);
1823 0 : stop_inquiry_at (e,
1824 : a);
1825 : }
1826 : else /* is_eligible */
1827 0 : start_inquiry (e,
1828 : a);
1829 : }
1830 0 : TALER_EXCHANGE_keys_decref (old_keys);
1831 0 : return;
1832 : }
1833 : }
1834 14 : e = GNUNET_new (struct Exchange);
1835 14 : e->keys = keys;
1836 14 : GNUNET_CONTAINER_DLL_insert (e_head,
1837 : e_tail,
1838 : e);
1839 14 : for (struct Account *a = a_head;
1840 14 : NULL != a;
1841 0 : a = a->next)
1842 : {
1843 0 : if (a->account_gen != database_gen)
1844 0 : continue;
1845 0 : if (is_eligible (e->keys,
1846 : a))
1847 : {
1848 0 : start_inquiry (e,
1849 : a);
1850 : }
1851 : else
1852 : {
1853 0 : flag_ineligible (a->instance_id,
1854 0 : e->keys->exchange_url,
1855 0 : &a->h_wire);
1856 : }
1857 : }
1858 : }
1859 :
1860 :
1861 : /**
1862 : * Function called when keys were changed in the
1863 : * merchant database. Updates ours.
1864 : *
1865 : * @param cls closure (NULL)
1866 : * @param extra additional event data provided
1867 : * @param extra_size number of bytes in @a extra
1868 : */
1869 : static void
1870 13 : keys_changed (void *cls,
1871 : const void *extra,
1872 : size_t extra_size)
1873 : {
1874 13 : const char *url = extra;
1875 :
1876 : (void) cls;
1877 13 : if ( (NULL == extra) ||
1878 : (0 == extra_size) )
1879 : {
1880 0 : GNUNET_break (0);
1881 0 : global_ret = EXIT_FAILURE;
1882 0 : GNUNET_SCHEDULER_shutdown ();
1883 0 : return;
1884 : }
1885 13 : if ('\0' != url[extra_size - 1])
1886 : {
1887 0 : GNUNET_break (0);
1888 0 : global_ret = EXIT_FAILURE;
1889 0 : GNUNET_SCHEDULER_shutdown ();
1890 0 : return;
1891 : }
1892 13 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1893 : "Received keys change notification: reload `%s'\n",
1894 : url);
1895 13 : find_keys (url);
1896 : }
1897 :
1898 :
1899 : /**
1900 : * Function called when a KYC rule was triggered by
1901 : * a transaction and we need to get the latest KYC
1902 : * status immediately.
1903 : *
1904 : * @param cls closure (NULL)
1905 : * @param extra additional event data provided
1906 : * @param extra_size number of bytes in @a extra
1907 : */
1908 : static void
1909 0 : rule_triggered (void *cls,
1910 : const void *extra,
1911 : size_t extra_size)
1912 : {
1913 0 : const char *text = extra;
1914 : const char *space;
1915 : struct TALER_MerchantWireHashP h_wire;
1916 : const char *exchange_url;
1917 :
1918 : (void) cls;
1919 0 : if ( (NULL == extra) ||
1920 : (0 == extra_size) )
1921 : {
1922 0 : GNUNET_break (0);
1923 0 : global_ret = EXIT_FAILURE;
1924 0 : GNUNET_SCHEDULER_shutdown ();
1925 0 : return;
1926 : }
1927 0 : if ('\0' != text[extra_size - 1])
1928 : {
1929 0 : GNUNET_break (0);
1930 0 : global_ret = EXIT_FAILURE;
1931 0 : GNUNET_SCHEDULER_shutdown ();
1932 0 : return;
1933 : }
1934 0 : space = memchr (extra,
1935 : ' ',
1936 : extra_size);
1937 0 : if (NULL == space)
1938 : {
1939 0 : GNUNET_break (0);
1940 0 : global_ret = EXIT_FAILURE;
1941 0 : GNUNET_SCHEDULER_shutdown ();
1942 0 : return;
1943 : }
1944 0 : if (GNUNET_OK !=
1945 0 : GNUNET_STRINGS_string_to_data (extra,
1946 0 : space - text,
1947 : &h_wire,
1948 : sizeof (h_wire)))
1949 : {
1950 0 : GNUNET_break (0);
1951 0 : global_ret = EXIT_FAILURE;
1952 0 : GNUNET_SCHEDULER_shutdown ();
1953 0 : return;
1954 : }
1955 0 : exchange_url = &space[1];
1956 0 : if (! TALER_is_web_url (exchange_url))
1957 : {
1958 0 : GNUNET_break (0);
1959 0 : global_ret = EXIT_FAILURE;
1960 0 : GNUNET_SCHEDULER_shutdown ();
1961 0 : return;
1962 : }
1963 :
1964 0 : for (struct Account *a = a_head;
1965 0 : NULL != a;
1966 0 : a = a->next)
1967 : {
1968 0 : if (0 !=
1969 0 : GNUNET_memcmp (&h_wire,
1970 : &a->h_wire))
1971 0 : continue;
1972 0 : for (struct Inquiry *i = a->i_head;
1973 0 : NULL != i;
1974 0 : i = i->next)
1975 : {
1976 0 : if (0 != strcmp (exchange_url,
1977 0 : i->e->keys->exchange_url))
1978 0 : continue;
1979 0 : i->kyc_ok = false;
1980 0 : if (inquiry_busy (i))
1981 : {
1982 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1983 : "/kyc-check already running for %s\n",
1984 : text);
1985 0 : return;
1986 : }
1987 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1988 : "Starting %skyc-check for `%s' due to KYC rule trigger\n",
1989 : exchange_url,
1990 : i->a->merchant_account_uri.full_payto);
1991 0 : request_inquiry (i);
1992 0 : return;
1993 : }
1994 : }
1995 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1996 : "KYC rule trigger notification `%s' matches none of our accounts\n",
1997 : text);
1998 : }
1999 :
2000 :
2001 : /**
2002 : * Function called on each configuration section. Finds sections
2003 : * about exchanges, parses the entries.
2004 : *
2005 : * @param cls NULL
2006 : * @param section name of the section
2007 : */
2008 : static void
2009 779 : accept_exchanges (void *cls,
2010 : const char *section)
2011 : {
2012 : char *url;
2013 :
2014 : (void) cls;
2015 779 : if (0 !=
2016 779 : strncasecmp (section,
2017 : "merchant-exchange-",
2018 : strlen ("merchant-exchange-")))
2019 760 : return;
2020 57 : if (GNUNET_YES ==
2021 57 : GNUNET_CONFIGURATION_get_value_yesno (cfg,
2022 : section,
2023 : "DISABLED"))
2024 38 : return;
2025 19 : if (GNUNET_OK !=
2026 19 : GNUNET_CONFIGURATION_get_value_string (cfg,
2027 : section,
2028 : "EXCHANGE_BASE_URL",
2029 : &url))
2030 : {
2031 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
2032 : section,
2033 : "EXCHANGE_BASE_URL");
2034 0 : global_ret = EXIT_NOTCONFIGURED;
2035 0 : GNUNET_SCHEDULER_shutdown ();
2036 0 : return;
2037 : }
2038 19 : find_keys (url);
2039 19 : GNUNET_free (url);
2040 : }
2041 :
2042 :
2043 : /**
2044 : * We're being aborted with CTRL-C (or SIGTERM). Shut down.
2045 : *
2046 : * @param cls closure (NULL)
2047 : */
2048 : static void
2049 19 : shutdown_task (void *cls)
2050 : {
2051 : (void) cls;
2052 19 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
2053 : "Running shutdown\n");
2054 19 : if (NULL != refresh_task)
2055 : {
2056 0 : GNUNET_SCHEDULER_cancel (refresh_task);
2057 0 : refresh_task = NULL;
2058 : }
2059 19 : while (NULL != refresh_head)
2060 : {
2061 0 : struct Refresh *r = refresh_head;
2062 :
2063 0 : GNUNET_CONTAINER_DLL_remove (refresh_head, refresh_tail, r);
2064 0 : GNUNET_free (r);
2065 : }
2066 19 : GNUNET_CONTAINER_multihashmap_destroy (refresh_map);
2067 33 : while (NULL != e_head)
2068 : {
2069 14 : struct Exchange *e = e_head;
2070 :
2071 14 : if (NULL != e->keys)
2072 : {
2073 14 : TALER_EXCHANGE_keys_decref (e->keys);
2074 14 : e->keys = NULL;
2075 : }
2076 14 : GNUNET_CONTAINER_DLL_remove (e_head,
2077 : e_tail,
2078 : e);
2079 14 : GNUNET_free (e);
2080 : }
2081 47 : while (NULL != a_head)
2082 : {
2083 28 : struct Account *a = a_head;
2084 :
2085 28 : stop_inquiries (a);
2086 28 : GNUNET_CONTAINER_DLL_remove (a_head,
2087 : a_tail,
2088 : a);
2089 28 : GNUNET_free (a->merchant_account_uri.full_payto);
2090 28 : GNUNET_free (a->instance_id);
2091 28 : GNUNET_free (a);
2092 : }
2093 19 : GNUNET_CONTAINER_multihashmap_destroy (inquiry_map);
2094 19 : if (NULL != eh_accounts)
2095 : {
2096 19 : TALER_MERCHANTDB_event_listen_cancel (eh_accounts);
2097 19 : eh_accounts = NULL;
2098 : }
2099 19 : if (NULL != account_task)
2100 : {
2101 0 : GNUNET_SCHEDULER_cancel (account_task);
2102 0 : account_task = NULL;
2103 : }
2104 19 : if (NULL != eh_keys)
2105 : {
2106 19 : TALER_MERCHANTDB_event_listen_cancel (eh_keys);
2107 19 : eh_keys = NULL;
2108 : }
2109 19 : if (NULL != eh_rule)
2110 : {
2111 19 : TALER_MERCHANTDB_event_listen_cancel (eh_rule);
2112 19 : eh_rule = NULL;
2113 : }
2114 19 : if (NULL != eh_update_forced)
2115 : {
2116 19 : TALER_MERCHANTDB_event_listen_cancel (eh_update_forced);
2117 19 : eh_update_forced = NULL;
2118 : }
2119 19 : if (NULL != keys_rule)
2120 : {
2121 0 : TALER_MERCHANTDB_event_listen_cancel (keys_rule);
2122 0 : keys_rule = NULL;
2123 : }
2124 19 : if (NULL != pg)
2125 : {
2126 19 : TALER_MERCHANTDB_disconnect (pg);
2127 19 : pg = NULL;
2128 : }
2129 19 : cfg = NULL;
2130 19 : if (NULL != ctx)
2131 : {
2132 19 : GNUNET_CURL_fini (ctx);
2133 19 : ctx = NULL;
2134 : }
2135 19 : if (NULL != rc)
2136 : {
2137 19 : GNUNET_CURL_gnunet_rc_destroy (rc);
2138 19 : rc = NULL;
2139 : }
2140 19 : }
2141 :
2142 :
2143 : /**
2144 : * Function called when we urgently need to re-check the KYC status
2145 : * of some account. Finds the respective inquiry and re-launches
2146 : * the check, unless we are already doing it.
2147 : *
2148 : * @param cls NULL
2149 : * @param instance_id instance for which to force the check
2150 : * @param exchange_url base URL of the exchange to check
2151 : * @param h_wire hash of the wire account to check KYC status for
2152 : */
2153 : static void
2154 13 : force_check_now (void *cls,
2155 : const char *instance_id,
2156 : const char *exchange_url,
2157 : const struct TALER_MerchantWireHashP *h_wire)
2158 : {
2159 : struct GNUNET_HashCode key;
2160 : struct Inquiry *i;
2161 :
2162 : (void) cls;
2163 13 : inquiry_key (instance_id, h_wire, exchange_url, &key);
2164 13 : i = GNUNET_CONTAINER_multihashmap_get (inquiry_map, &key);
2165 13 : if (NULL == i)
2166 : {
2167 : /* Account discovery and exchange-key loading recover the persisted due
2168 : time. Absence from the index does not prove the account ineligible. */
2169 1 : return;
2170 : }
2171 12 : GNUNET_assert (0 == strcmp (instance_id, i->a->instance_id));
2172 12 : GNUNET_assert (0 == GNUNET_memcmp (h_wire, &i->a->h_wire));
2173 12 : GNUNET_assert (0 == strcmp (exchange_url, i->e->keys->exchange_url));
2174 12 : if (i->a->account_gen != database_gen)
2175 0 : return;
2176 12 : request_inquiry (i);
2177 : }
2178 :
2179 :
2180 : /**
2181 : * Process one instance per scheduler turn, after pending account discovery.
2182 : */
2183 : static void
2184 8 : process_refresh (void *cls)
2185 : {
2186 8 : struct Refresh *r = refresh_head;
2187 : enum GNUNET_DB_QueryStatus qs;
2188 :
2189 : (void) cls;
2190 8 : refresh_task = NULL;
2191 8 : if (NULL != account_task)
2192 : {
2193 0 : refresh_task = GNUNET_SCHEDULER_add_now (&process_refresh, NULL);
2194 0 : return;
2195 : }
2196 8 : GNUNET_assert (NULL != r);
2197 8 : GNUNET_CONTAINER_DLL_remove (refresh_head, refresh_tail, r);
2198 8 : GNUNET_assert (GNUNET_YES ==
2199 : GNUNET_CONTAINER_multihashmap_remove (refresh_map,
2200 : &r->key,
2201 : r));
2202 8 : qs = TALER_MERCHANTDB_iterate_outdated_kyc_statuses (
2203 : pg,
2204 : r->merchant_serial,
2205 : &force_check_now,
2206 : NULL);
2207 8 : GNUNET_free (r);
2208 8 : if (qs < 0)
2209 : {
2210 0 : GNUNET_break (0);
2211 0 : global_ret = EXIT_FAILURE;
2212 0 : GNUNET_SCHEDULER_shutdown ();
2213 0 : return;
2214 : }
2215 8 : if (NULL != refresh_head)
2216 0 : refresh_task = GNUNET_SCHEDULER_add_now (&process_refresh, NULL);
2217 : }
2218 :
2219 :
2220 : /**
2221 : * Queue an instance refresh. The notification payload is exactly one
2222 : * unsigned 64-bit merchant serial in network byte order.
2223 : */
2224 : static void
2225 14 : update_forced (void *cls,
2226 : const void *extra,
2227 : size_t extra_size)
2228 : {
2229 : uint64_t serial;
2230 : struct GNUNET_HashCode key;
2231 : struct Refresh *r;
2232 :
2233 : (void) cls;
2234 14 : if ( (NULL == extra) || (sizeof (serial) != extra_size) )
2235 : {
2236 2 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2237 : "Ignoring KYC refresh notification with invalid payload size\n");
2238 4 : return;
2239 : }
2240 12 : memcpy (&serial, extra, sizeof (serial));
2241 12 : GNUNET_CRYPTO_hash (&serial, sizeof (serial), &key);
2242 12 : serial = GNUNET_ntohll (serial);
2243 12 : if ( (0 == serial) || (serial > INT64_MAX) )
2244 : {
2245 1 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2246 : "Ignoring KYC refresh notification with invalid instance serial\n");
2247 1 : return;
2248 : }
2249 11 : if (GNUNET_CONTAINER_multihashmap_contains (refresh_map, &key))
2250 1 : return;
2251 10 : r = GNUNET_new (struct Refresh);
2252 10 : r->key = key;
2253 10 : r->merchant_serial = serial;
2254 10 : GNUNET_CONTAINER_DLL_insert_tail (refresh_head, refresh_tail, r);
2255 10 : GNUNET_assert (GNUNET_OK ==
2256 : GNUNET_CONTAINER_multihashmap_put (
2257 : refresh_map, &r->key, r,
2258 : GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY));
2259 10 : if (NULL == refresh_task)
2260 9 : refresh_task = GNUNET_SCHEDULER_add_now (&process_refresh, NULL);
2261 : }
2262 :
2263 : /**
2264 : * First task.
2265 : *
2266 : * @param cls closure, NULL
2267 : * @param args remaining command-line arguments
2268 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
2269 : * @param c configuration
2270 : */
2271 : static void
2272 19 : run (void *cls,
2273 : char *const *args,
2274 : const char *cfgfile,
2275 : const struct GNUNET_CONFIGURATION_Handle *c)
2276 : {
2277 : (void) args;
2278 : (void) cfgfile;
2279 :
2280 19 : cfg = c;
2281 19 : inquiry_map = GNUNET_CONTAINER_multihashmap_create (256, GNUNET_YES);
2282 19 : refresh_map = GNUNET_CONTAINER_multihashmap_create (32, GNUNET_YES);
2283 19 : TALER_EXCHANGE_setup (enable_h3
2284 19 : ? TALER_EXCHANGE_GO_ENABLE_HTTP3
2285 : : TALER_EXCHANGE_GO_FORCE_HTTP1_1);
2286 19 : if (GNUNET_OK !=
2287 19 : GNUNET_CONFIGURATION_get_value_time (cfg,
2288 : "merchant-kyccheck",
2289 : "AML_FREQ",
2290 : &aml_freq))
2291 : {
2292 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
2293 : "merchant-kyccheck",
2294 : "AML_FREQ");
2295 : /* use default */
2296 0 : aml_freq = AML_FREQ;
2297 : }
2298 19 : if (GNUNET_OK !=
2299 19 : GNUNET_CONFIGURATION_get_value_time (cfg,
2300 : "merchant-kyccheck",
2301 : "AML_LOW_FREQ",
2302 : &aml_low_freq))
2303 : {
2304 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
2305 : "merchant-kyccheck",
2306 : "AML_LOW_FREQ");
2307 : /* use default */
2308 0 : aml_low_freq = AML_LOW_FREQ;
2309 : }
2310 19 : if (GNUNET_TIME_relative_cmp (aml_low_freq,
2311 : <,
2312 : aml_freq))
2313 : {
2314 0 : aml_low_freq = GNUNET_TIME_relative_multiply (aml_freq,
2315 : 10);
2316 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
2317 : "AML_LOW_FREQ was set to less than AML_FREQ. Using %s instead\n",
2318 : GNUNET_TIME_relative2s (aml_low_freq,
2319 : true));
2320 : }
2321 19 : GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
2322 : NULL);
2323 19 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
2324 : &rc);
2325 19 : if (NULL == ctx)
2326 : {
2327 0 : GNUNET_break (0);
2328 0 : GNUNET_SCHEDULER_shutdown ();
2329 0 : global_ret = EXIT_FAILURE;
2330 0 : return;
2331 : }
2332 19 : rc = GNUNET_CURL_gnunet_rc_create (ctx);
2333 19 : if (NULL ==
2334 19 : (pg = TALER_MERCHANTDB_connect (cfg)))
2335 : {
2336 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
2337 : "Failed to initialize DB subsystem. Consider running taler-merchant-dbconfig.\n");
2338 0 : GNUNET_SCHEDULER_shutdown ();
2339 0 : global_ret = EXIT_FAILURE;
2340 0 : return;
2341 : }
2342 : {
2343 19 : struct GNUNET_DB_EventHeaderP es = {
2344 19 : .size = htons (sizeof (es)),
2345 19 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
2346 : };
2347 :
2348 : eh_keys
2349 38 : = TALER_MERCHANTDB_event_listen (pg,
2350 : &es,
2351 19 : GNUNET_TIME_UNIT_FOREVER_REL,
2352 : &keys_changed,
2353 : NULL);
2354 : }
2355 : {
2356 19 : struct GNUNET_DB_EventHeaderP es = {
2357 19 : .size = htons (sizeof (es)),
2358 19 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_UPDATE_FORCED)
2359 : };
2360 :
2361 : eh_update_forced
2362 38 : = TALER_MERCHANTDB_event_listen (pg,
2363 : &es,
2364 19 : GNUNET_TIME_UNIT_FOREVER_REL,
2365 : &update_forced,
2366 : NULL);
2367 : }
2368 : {
2369 19 : struct GNUNET_DB_EventHeaderP es = {
2370 19 : .size = htons (sizeof (es)),
2371 19 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
2372 : };
2373 :
2374 : eh_rule
2375 38 : = TALER_MERCHANTDB_event_listen (pg,
2376 : &es,
2377 19 : GNUNET_TIME_UNIT_FOREVER_REL,
2378 : &rule_triggered,
2379 : NULL);
2380 : }
2381 19 : GNUNET_CONFIGURATION_iterate_sections (cfg,
2382 : &accept_exchanges,
2383 : NULL);
2384 : {
2385 19 : struct GNUNET_DB_EventHeaderP es = {
2386 19 : .size = htons (sizeof (es)),
2387 19 : .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
2388 : };
2389 :
2390 : eh_accounts
2391 38 : = TALER_MERCHANTDB_event_listen (pg,
2392 : &es,
2393 19 : GNUNET_TIME_UNIT_FOREVER_REL,
2394 : &account_changed,
2395 : NULL);
2396 : }
2397 19 : GNUNET_assert (NULL == account_task);
2398 : account_task
2399 19 : = GNUNET_SCHEDULER_add_now (&find_accounts,
2400 : NULL);
2401 : }
2402 :
2403 :
2404 : /**
2405 : * The main function of taler-merchant-kyccheck
2406 : *
2407 : * @param argc number of arguments from the command line
2408 : * @param argv command line arguments
2409 : * @return 0 ok, 1 on error
2410 : */
2411 : int
2412 19 : main (int argc,
2413 : char *const *argv)
2414 : {
2415 19 : struct GNUNET_GETOPT_CommandLineOption options[] = {
2416 19 : GNUNET_GETOPT_option_flag ('3',
2417 : "http3",
2418 : "enable support for HTTP/2 and HTTP/3",
2419 : &enable_h3),
2420 19 : GNUNET_GETOPT_option_timetravel ('T',
2421 : "timetravel"),
2422 19 : GNUNET_GETOPT_option_flag ('t',
2423 : "test",
2424 : "run in test mode and exit when idle",
2425 : &test_mode),
2426 19 : GNUNET_GETOPT_option_version (VERSION),
2427 : GNUNET_GETOPT_OPTION_END
2428 : };
2429 : enum GNUNET_GenericReturnValue ret;
2430 :
2431 19 : ret = GNUNET_PROGRAM_run (
2432 : TALER_MERCHANT_project_data (),
2433 : argc, argv,
2434 : "taler-merchant-kyccheck",
2435 : gettext_noop (
2436 : "background process that checks the KYC state of our bank accounts at various exchanges"),
2437 : options,
2438 : &run, NULL);
2439 19 : if (GNUNET_SYSERR == ret)
2440 0 : return EXIT_NOTCONFIGURED;
2441 19 : if (GNUNET_NO == ret)
2442 0 : return EXIT_SUCCESS;
2443 19 : return global_ret;
2444 : }
2445 :
2446 :
2447 : /* end of taler-merchant-kyccheck.c */
|