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 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 : #include "microhttpd.h"
23 : #include <gnunet/gnunet_util_lib.h>
24 : #include <jansson.h>
25 : #include <pthread.h>
26 : #include <regex.h>
27 : #include <taler/taler_dbevents.h>
28 : #include <taler/taler_json_lib.h>
29 : #include <taler/taler_exchange_service.h>
30 : #include "taler_merchant_util.h"
31 : #include "taler_merchant_bank_lib.h"
32 : #include "taler_merchantdb_lib.h"
33 : #include "taler_merchantdb_plugin.h"
34 :
35 : /**
36 : * Timeout for the exchange interaction. Rather long as we should do
37 : * long-polling and do not want to wake up too often.
38 : */
39 : #define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
40 : GNUNET_TIME_UNIT_MINUTES, \
41 : 30)
42 :
43 : /**
44 : * How long do we wait between requests if all we wait
45 : * for is a change in the AML investigation status?
46 : * Default value.
47 : */
48 : #define AML_FREQ GNUNET_TIME_relative_multiply ( \
49 : GNUNET_TIME_UNIT_HOURS, \
50 : 6)
51 :
52 : /**
53 : * How long do we wait between requests if all we wait
54 : * for is a change in the AML investigation status?
55 : */
56 : static struct GNUNET_TIME_Relative aml_freq;
57 :
58 : /**
59 : * How frequently do we check for updates to our KYC status
60 : * if there is no actual reason to check? Set to a very low
61 : * frequency, just to ensure we eventually notice.
62 : * Default value.
63 : */
64 : #define AML_LOW_FREQ GNUNET_TIME_relative_multiply ( \
65 : GNUNET_TIME_UNIT_DAYS, \
66 : 7)
67 :
68 : /**
69 : * How frequently do we check for updates to our KYC status
70 : * if there is no actual reason to check? Set to a very low
71 : * frequency, just to ensure we eventually notice.
72 : */
73 : static struct GNUNET_TIME_Relative aml_low_freq;
74 :
75 :
76 : /**
77 : * How many inquiries do we process concurrently at most.
78 : */
79 : #define OPEN_INQUIRY_LIMIT 1024
80 :
81 :
82 : /**
83 : * Information about an exchange.
84 : */
85 : struct Exchange
86 : {
87 : /**
88 : * Kept in a DLL.
89 : */
90 : struct Exchange *next;
91 :
92 : /**
93 : * Kept in a DLL.
94 : */
95 : struct Exchange *prev;
96 :
97 : /**
98 : * The keys of this exchange
99 : */
100 : struct TALER_EXCHANGE_Keys *keys;
101 :
102 : };
103 :
104 :
105 : /**
106 : * Information about an Account.
107 : */
108 : struct Account
109 : {
110 : /**
111 : * Kept in a DLL.
112 : */
113 : struct Account *next;
114 :
115 : /**
116 : * Kept in a DLL.
117 : */
118 : struct Account *prev;
119 :
120 : /**
121 : * Head of inquiries for this account.
122 : */
123 : struct Inquiry *i_head;
124 :
125 : /**
126 : * Tail of inquiries for this account.
127 : */
128 : struct Inquiry *i_tail;
129 :
130 : /**
131 : * Merchant instance this account belongs to.
132 : */
133 : char *instance_id;
134 :
135 : /**
136 : * The payto-URI of this account.
137 : */
138 : struct TALER_FullPayto merchant_account_uri;
139 :
140 : /**
141 : * Wire hash of the merchant bank account (with the
142 : * respective salt).
143 : */
144 : struct TALER_MerchantWireHashP h_wire;
145 :
146 : /**
147 : * Private key of the instance.
148 : */
149 : union TALER_AccountPrivateKeyP ap;
150 :
151 : /**
152 : * Hash of the @e merchant_account_uri.
153 : */
154 : struct TALER_NormalizedPaytoHashP h_payto;
155 :
156 : /**
157 : * Database generation when this account
158 : * was last active.
159 : */
160 : uint64_t account_gen;
161 :
162 : };
163 :
164 :
165 : /**
166 : * Information about an inquiry job.
167 : */
168 : struct Inquiry
169 : {
170 : /**
171 : * Kept in a DLL.
172 : */
173 : struct Inquiry *next;
174 :
175 : /**
176 : * Kept in a DLL.
177 : */
178 : struct Inquiry *prev;
179 :
180 : /**
181 : * Main task for this inquiry.
182 : */
183 : struct GNUNET_SCHEDULER_Task *task;
184 :
185 : /**
186 : * Which exchange is this inquiry about.
187 : */
188 : struct Exchange *e;
189 :
190 : /**
191 : * Which account is this inquiry about.
192 : */
193 : struct Account *a;
194 :
195 : /**
196 : * AccountLimits that apply to the account, NULL
197 : * if unknown.
198 : */
199 : json_t *jlimits;
200 :
201 : /**
202 : * Handle for the actual HTTP request to the exchange.
203 : */
204 : struct TALER_EXCHANGE_KycCheckHandle *kyc;
205 :
206 : /**
207 : * Access token for the /kyc-info API.
208 : */
209 : struct TALER_AccountAccessTokenP access_token;
210 :
211 : /**
212 : * Last time we called the /kyc-check endpoint.
213 : */
214 : struct GNUNET_TIME_Timestamp last_kyc_check;
215 :
216 : /**
217 : * When is the next KYC check due?
218 : */
219 : struct GNUNET_TIME_Absolute due;
220 :
221 : /**
222 : * When should the current KYC time out?
223 : */
224 : struct GNUNET_TIME_Absolute timeout;
225 :
226 : /**
227 : * Current exponential backoff.
228 : */
229 : struct GNUNET_TIME_Relative backoff;
230 :
231 : /**
232 : * Rule generation known to the client, 0 for none.
233 : * Corresponds to the decision row in the exchange.
234 : */
235 : uint64_t rule_gen;
236 :
237 : /**
238 : * Last HTTP status returned by the exchange from
239 : * the /kyc-check endpoint.
240 : */
241 : unsigned int last_http_status;
242 :
243 : /**
244 : * Last Taler error code returned by the exchange from
245 : * the /kyc-check endpoint.
246 : */
247 : enum TALER_ErrorCode last_ec;
248 :
249 : /**
250 : * True if this is not our first time we make this request.
251 : */
252 : bool not_first_time;
253 :
254 : /**
255 : * Do soft limits on transactions apply to this merchant for operations
256 : * merchants care about? If so, we should increase our request frequency
257 : * and ask more often to see if they were lifted.
258 : */
259 : bool zero_limited;
260 :
261 : /**
262 : * Did we not run this inquiry due to limits?
263 : */
264 : bool limited;
265 :
266 : /**
267 : * Do we believe this account's KYC is in good shape?
268 : */
269 : bool kyc_ok;
270 :
271 : /**
272 : * True if merchant did perform this account's KYC AUTH transfer and @e access_token is set.
273 : */
274 : bool auth_ok;
275 :
276 : /**
277 : * True if the account is known to be currently under
278 : * investigation by AML staff.
279 : */
280 : bool aml_review;
281 :
282 : };
283 :
284 :
285 : /**
286 : * Head of known exchanges.
287 : */
288 : static struct Exchange *e_head;
289 :
290 : /**
291 : * Tail of known exchanges.
292 : */
293 : static struct Exchange *e_tail;
294 :
295 : /**
296 : * Head of accounts.
297 : */
298 : static struct Account *a_head;
299 :
300 : /**
301 : * Tail of accounts.
302 : */
303 : static struct Account *a_tail;
304 :
305 : /**
306 : * The merchant's configuration.
307 : */
308 : static const struct GNUNET_CONFIGURATION_Handle *cfg;
309 :
310 : /**
311 : * Our database plugin.
312 : */
313 : static struct TALER_MERCHANTDB_Plugin *db_plugin;
314 :
315 : /**
316 : * Handle to the context for interacting with the bank.
317 : */
318 : static struct GNUNET_CURL_Context *ctx;
319 :
320 : /**
321 : * Scheduler context for running the @e ctx.
322 : */
323 : static struct GNUNET_CURL_RescheduleContext *rc;
324 :
325 : /**
326 : * Event handler to learn that there may be new bank
327 : * accounts to check.
328 : */
329 : static struct GNUNET_DB_EventHandler *eh_accounts;
330 :
331 : /**
332 : * Event handler to learn that there may be new exchange
333 : * keys to check.
334 : */
335 : static struct GNUNET_DB_EventHandler *eh_keys;
336 :
337 : /**
338 : * Event handler to learn that there was a KYC
339 : * rule triggered and we need to check the KYC
340 : * status for an account.
341 : */
342 : static struct GNUNET_DB_EventHandler *eh_rule;
343 :
344 : /**
345 : * Main task to discover (new) accounts.
346 : */
347 : static struct GNUNET_SCHEDULER_Task *account_task;
348 :
349 : /**
350 : * Counter determining how often we have called
351 : * "select_accounts" on the database.
352 : */
353 : static uint64_t database_gen;
354 :
355 : /**
356 : * How many active inquiries do we have right now.
357 : */
358 : static unsigned int active_inquiries;
359 :
360 : /**
361 : * Value to return from main(). 0 on success, non-zero on errors.
362 : */
363 : static int global_ret;
364 :
365 : /**
366 : * #GNUNET_YES if we are in test mode and should exit when idle.
367 : */
368 : static int test_mode;
369 :
370 : /**
371 : * True if the last DB query was limited by the
372 : * #OPEN_INQUIRY_LIMIT and we thus should check again
373 : * as soon as we are substantially below that limit,
374 : * and not only when we get a DB notification.
375 : */
376 : static bool at_limit;
377 :
378 :
379 : /**
380 : * Check about performing a /kyc-check request with the
381 : * exchange for the given inquiry.
382 : *
383 : * @param cls a `struct Inquiry` to process
384 : */
385 : static void
386 : inquiry_work (void *cls);
387 :
388 :
389 : /**
390 : * An inquiry finished, check if we should resume others.
391 : */
392 : static void
393 15 : end_inquiry (void)
394 : {
395 15 : GNUNET_assert (active_inquiries > 0);
396 15 : active_inquiries--;
397 15 : if ( (active_inquiries < OPEN_INQUIRY_LIMIT / 2) &&
398 : (at_limit) )
399 : {
400 0 : at_limit = false;
401 0 : for (struct Account *a = a_head;
402 0 : NULL != a;
403 0 : a = a->next)
404 : {
405 0 : for (struct Inquiry *i = a->i_head;
406 0 : NULL != i;
407 0 : i = i->next)
408 : {
409 0 : if (! i->limited)
410 0 : continue;
411 0 : GNUNET_assert (NULL == i->task);
412 : /* done synchronously so that the active_inquiries
413 : is updated immediately */
414 0 : inquiry_work (i);
415 0 : if (at_limit)
416 0 : break;
417 : }
418 0 : if (at_limit)
419 0 : break;
420 : }
421 : }
422 15 : if ( (! at_limit) &&
423 15 : (0 == active_inquiries) &&
424 : (test_mode) )
425 : {
426 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
427 : "No more open inquiries and in test mode. Existing.\n");
428 0 : GNUNET_SCHEDULER_shutdown ();
429 0 : return;
430 : }
431 : }
432 :
433 :
434 : /**
435 : * Pack the given @a limit into the JSON @a limits array.
436 : *
437 : * @param limit account limit to pack
438 : * @param[in,out] limits JSON array to extend
439 : */
440 : static void
441 2 : pack_limit (const struct TALER_EXCHANGE_AccountLimit *limit,
442 : json_t *limits)
443 : {
444 : json_t *jl;
445 :
446 2 : jl = GNUNET_JSON_PACK (
447 : TALER_JSON_pack_kycte ("operation_type",
448 : limit->operation_type),
449 : GNUNET_JSON_pack_time_rel ("timeframe",
450 : limit->timeframe),
451 : TALER_JSON_pack_amount ("threshold",
452 : &limit->threshold),
453 : GNUNET_JSON_pack_bool ("soft_limit",
454 : limit->soft_limit)
455 : );
456 2 : GNUNET_assert (0 ==
457 : json_array_append_new (limits,
458 : jl));
459 2 : }
460 :
461 :
462 : /**
463 : * Update KYC status for @a i based on
464 : * @a account_kyc_status
465 : *
466 : * @param[in,out] i inquiry context, jlimits is updated
467 : * @param account_kyc_status account KYC status details
468 : */
469 : static void
470 2 : store_kyc_status (
471 : struct Inquiry *i,
472 : const struct TALER_EXCHANGE_AccountKycStatus *account_kyc_status)
473 : {
474 : json_t *jlimits;
475 :
476 2 : json_decref (i->jlimits);
477 2 : jlimits = json_array ();
478 2 : GNUNET_assert (NULL != jlimits);
479 2 : i->zero_limited = false;
480 4 : for (unsigned int j = 0; j<account_kyc_status->limits_length; j++)
481 : {
482 2 : const struct TALER_EXCHANGE_AccountLimit *limit
483 2 : = &account_kyc_status->limits[j];
484 :
485 2 : pack_limit (limit,
486 : jlimits);
487 2 : if (TALER_amount_is_zero (&limit->threshold) &&
488 2 : limit->soft_limit &&
489 2 : ( (TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT == limit->operation_type) ||
490 2 : (TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE == limit->operation_type) ||
491 0 : (TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION == limit->operation_type) ) )
492 : {
493 2 : i->zero_limited = true;
494 : }
495 : }
496 2 : i->jlimits = jlimits;
497 2 : GNUNET_break (! GNUNET_is_zero (&account_kyc_status->access_token));
498 2 : i->access_token = account_kyc_status->access_token;
499 2 : i->auth_ok = true;
500 2 : i->aml_review = account_kyc_status->aml_review;
501 2 : i->kyc_ok = (MHD_HTTP_OK == i->last_http_status);
502 2 : }
503 :
504 :
505 : /**
506 : * Function called with the result of a KYC check.
507 : *
508 : * @param cls a `struct Inquiry *`
509 : * @param ks the account's KYC status details
510 : */
511 : static void
512 15 : exchange_check_cb (
513 : void *cls,
514 : const struct TALER_EXCHANGE_KycStatus *ks)
515 : {
516 15 : struct Inquiry *i = cls;
517 15 : bool progress = false;
518 :
519 15 : if (! i->not_first_time)
520 13 : progress = true;
521 15 : i->kyc = NULL;
522 15 : i->last_http_status = ks->hr.http_status;
523 15 : i->last_ec = ks->hr.ec;
524 15 : i->rule_gen = 0;
525 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
526 : "Checking KYC status of `%s' at `%s' is %u\n",
527 : i->a->merchant_account_uri.full_payto,
528 : i->e->keys->exchange_url,
529 : ks->hr.http_status);
530 15 : switch (ks->hr.http_status)
531 : {
532 2 : case MHD_HTTP_OK:
533 2 : if (! i->kyc_ok)
534 1 : progress = true;
535 2 : i->rule_gen = ks->details.ok.rule_gen;
536 2 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
537 : /* exchange says KYC is OK, gives status information */
538 2 : store_kyc_status (i,
539 : &ks->details.ok);
540 2 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
541 2 : if (i->aml_review || i->zero_limited)
542 : {
543 2 : if (! progress)
544 1 : i->due = GNUNET_TIME_relative_to_absolute (
545 : GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_freq,
546 : i->backoff)));
547 : }
548 : else
549 : {
550 : /* KYC is OK, only check again if triggered */
551 0 : i->due = GNUNET_TIME_relative_to_absolute (
552 : GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq,
553 : i->backoff)));
554 : }
555 2 : break;
556 0 : case MHD_HTTP_ACCEPTED:
557 0 : progress = ! i->auth_ok;
558 0 : i->rule_gen = ks->details.ok.rule_gen;
559 0 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
560 :
561 : /* exchange says KYC is required */
562 0 : store_kyc_status (i,
563 : &ks->details.accepted);
564 0 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
565 : /* Start immediately with long-polling */
566 0 : if (! progress)
567 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
568 : i->timeout);
569 0 : break;
570 12 : case MHD_HTTP_NO_CONTENT:
571 12 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
572 12 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
573 : /* exchange claims KYC is off! */
574 12 : i->kyc_ok = true;
575 12 : i->aml_review = false;
576 : /* Clear limits, in case exchange had KYC on previously */
577 12 : json_decref (i->jlimits);
578 12 : i->jlimits = NULL;
579 : /* KYC is OK, only check again if triggered */
580 12 : i->due = GNUNET_TIME_relative_to_absolute (
581 : GNUNET_TIME_randomize (GNUNET_TIME_relative_max (aml_low_freq,
582 : i->backoff)));
583 12 : break;
584 0 : case MHD_HTTP_FORBIDDEN: /* bad signature */
585 0 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
586 : /* Forbidden => KYC auth must be wrong */
587 0 : i->auth_ok = false;
588 : /* Start with long-polling */
589 0 : if (! progress)
590 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
591 : i->timeout);
592 0 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
593 0 : break;
594 1 : case MHD_HTTP_NOT_FOUND: /* account unknown */
595 1 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
596 : /* Account unknown => no KYC auth yet */
597 1 : i->auth_ok = false;
598 : /* unknown account => wire transfer required! */
599 1 : i->kyc_ok = false;
600 : /* There should not be any limits yet, but clear them
601 : just in case the exchange has amnesia */
602 1 : json_decref (i->jlimits);
603 1 : i->jlimits = NULL;
604 : /* Start immediately with Long-polling */
605 1 : if (! progress)
606 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
607 : i->timeout);
608 1 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
609 1 : break;
610 0 : case MHD_HTTP_CONFLICT: /* no account_pub known */
611 0 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
612 : /* Conflict => KYC auth wire transfer missing! */
613 0 : i->auth_ok = false;
614 : /* Start immediately with Long-polling */
615 0 : if (! progress)
616 0 : i->due = GNUNET_TIME_absolute_max (i->last_kyc_check.abs_time,
617 : i->timeout);
618 0 : i->backoff = GNUNET_TIME_UNIT_MINUTES;
619 0 : break;
620 0 : default:
621 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
622 : "Exchange responded with HTTP status %u (%d) to /kyc-check request!\n",
623 : ks->hr.http_status,
624 : ks->hr.ec);
625 : i->backoff
626 0 : = GNUNET_TIME_randomized_backoff (i->backoff,
627 : EXCHANGE_TIMEOUT);
628 0 : i->last_kyc_check = GNUNET_TIME_timestamp_get ();
629 0 : i->due = GNUNET_TIME_relative_to_absolute (i->backoff);
630 0 : i->auth_ok = false;
631 0 : break;
632 : }
633 :
634 : {
635 : enum GNUNET_DB_QueryStatus qs;
636 :
637 15 : qs = db_plugin->account_kyc_set_status (
638 15 : db_plugin->cls,
639 15 : i->a->instance_id,
640 15 : &i->a->h_wire,
641 15 : i->e->keys->exchange_url,
642 : i->last_kyc_check,
643 : i->last_http_status,
644 : i->last_ec,
645 : i->rule_gen,
646 15 : (i->auth_ok)
647 : ? &i->access_token
648 : : NULL,
649 15 : i->jlimits,
650 15 : i->aml_review,
651 15 : i->kyc_ok);
652 15 : if (qs < 0)
653 : {
654 0 : GNUNET_break (0);
655 0 : global_ret = EXIT_FAILURE;
656 0 : GNUNET_SCHEDULER_shutdown ();
657 0 : return;
658 : }
659 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
660 : "account_set_kyc_status (%s, %u, %s, %s) returned %d\n",
661 : i->e->keys->exchange_url,
662 : i->last_http_status,
663 : i->auth_ok ? "auth OK" : "auth needed",
664 : NULL == i->jlimits ? "default limits" : "custom limits",
665 : (int) qs);
666 15 : i->not_first_time = true;
667 : }
668 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
669 : "Will repeat inquiry in %s\n",
670 : GNUNET_TIME_relative2s (
671 : GNUNET_TIME_absolute_get_remaining (i->due),
672 : true));
673 15 : if (! GNUNET_TIME_absolute_is_never (i->due))
674 15 : i->task = GNUNET_SCHEDULER_add_at (i->due,
675 : &inquiry_work,
676 : i);
677 15 : end_inquiry ();
678 : }
679 :
680 :
681 : static void
682 15 : inquiry_work (void *cls)
683 : {
684 15 : struct Inquiry *i = cls;
685 : enum TALER_EXCHANGE_KycLongPollTarget lpt;
686 :
687 15 : i->task = NULL;
688 15 : if (! GNUNET_TIME_absolute_is_past (i->due))
689 : {
690 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
691 : "Will start inquiry on %s for %s in %s\n",
692 : i->a->merchant_account_uri.full_payto,
693 : i->e->keys->exchange_url,
694 : GNUNET_TIME_relative2s (
695 : GNUNET_TIME_absolute_get_remaining (i->due),
696 : true));
697 : i->task
698 0 : = GNUNET_SCHEDULER_add_at (i->due,
699 : &inquiry_work,
700 : i);
701 0 : goto finish;
702 : }
703 :
704 15 : GNUNET_assert (OPEN_INQUIRY_LIMIT >= active_inquiries);
705 15 : if (OPEN_INQUIRY_LIMIT <= active_inquiries)
706 : {
707 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
708 : "Not looking for work: at limit\n");
709 0 : i->limited = true;
710 0 : at_limit = true;
711 0 : return;
712 : }
713 15 : at_limit = false;
714 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
715 : "Checking KYC status of `%s' at `%s'\n",
716 : i->a->merchant_account_uri.full_payto,
717 : i->e->keys->exchange_url);
718 : i->timeout
719 15 : = GNUNET_TIME_relative_to_absolute (EXCHANGE_TIMEOUT);
720 15 : lpt = TALER_EXCHANGE_KLPT_NONE;
721 15 : if (! i->auth_ok)
722 14 : lpt = TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER;
723 1 : else if (! i->kyc_ok)
724 0 : lpt = TALER_EXCHANGE_KLPT_KYC_OK;
725 1 : else if (i->aml_review)
726 0 : lpt = TALER_EXCHANGE_KLPT_INVESTIGATION_DONE;
727 15 : if (! i->not_first_time)
728 13 : lpt = TALER_EXCHANGE_KLPT_NONE;
729 13 : i->kyc = TALER_EXCHANGE_kyc_check (
730 : ctx,
731 15 : i->e->keys->exchange_url,
732 15 : &i->a->h_payto,
733 15 : &i->a->ap,
734 : i->rule_gen,
735 : lpt,
736 15 : i->not_first_time && (! test_mode)
737 2 : ? EXCHANGE_TIMEOUT
738 : : GNUNET_TIME_UNIT_ZERO,
739 : &exchange_check_cb,
740 : i);
741 15 : if (NULL == i->kyc)
742 : {
743 0 : GNUNET_break (0);
744 0 : i->due = i->timeout;
745 : i->task
746 0 : = GNUNET_SCHEDULER_add_at (i->due,
747 : &inquiry_work,
748 : i);
749 0 : goto finish;
750 : }
751 15 : active_inquiries++;
752 15 : finish:
753 15 : if ( (0 == active_inquiries) &&
754 : (test_mode) )
755 : {
756 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
757 : "No more open inquiries and in test mode. Existing.\n");
758 0 : GNUNET_SCHEDULER_shutdown ();
759 0 : return;
760 : }
761 : }
762 :
763 :
764 : /**
765 : * Check if the account @a could work with exchange that
766 : * has keys @a keys.
767 : *
768 : * @param keys the keys of an exchange
769 : * @param a an account
770 : */
771 : static bool
772 14 : is_eligible (const struct TALER_EXCHANGE_Keys *keys,
773 : const struct Account *a)
774 : {
775 : struct TALER_NormalizedPayto np;
776 : bool ret;
777 :
778 14 : np = TALER_payto_normalize (a->merchant_account_uri);
779 14 : ret = TALER_EXCHANGE_keys_test_account_allowed (keys,
780 : true,
781 : np);
782 14 : GNUNET_free (np.normalized_payto);
783 14 : return ret;
784 : }
785 :
786 :
787 : /**
788 : * Start the KYC checking for account @a at exchange @a e.
789 : *
790 : * @param e an exchange
791 : * @param a an account
792 : */
793 : static void
794 13 : start_inquiry (struct Exchange *e,
795 : struct Account *a)
796 : {
797 : struct Inquiry *i;
798 : enum GNUNET_DB_QueryStatus qs;
799 :
800 13 : i = GNUNET_new (struct Inquiry);
801 13 : i->e = e;
802 13 : i->a = a;
803 13 : GNUNET_CONTAINER_DLL_insert (a->i_head,
804 : a->i_tail,
805 : i);
806 13 : qs = db_plugin->get_kyc_status (db_plugin->cls,
807 : a->merchant_account_uri,
808 13 : a->instance_id,
809 13 : e->keys->exchange_url,
810 : &i->auth_ok,
811 : &i->access_token,
812 : &i->kyc_ok,
813 : &i->last_http_status,
814 : &i->last_ec,
815 : &i->rule_gen,
816 : &i->last_kyc_check,
817 : &i->aml_review,
818 : &i->jlimits);
819 13 : if (qs < 0)
820 : {
821 0 : GNUNET_break (0);
822 0 : global_ret = EXIT_FAILURE;
823 0 : GNUNET_SCHEDULER_shutdown ();
824 0 : return;
825 : }
826 13 : if (qs > 0)
827 0 : i->not_first_time = true;
828 13 : switch (i->last_http_status)
829 : {
830 0 : case MHD_HTTP_OK:
831 : /* KYC is OK, but we could have missed some triggers,
832 : so let's check, but slowly within the next minute
833 : so that we do not overdo it if this process happens
834 : to be restarted a lot. */
835 0 : if (GNUNET_YES != test_mode)
836 : {
837 0 : i->due = GNUNET_TIME_relative_to_absolute (
838 : GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES));
839 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
840 : "Previous KYC status is OK, randomizing inquiry to start at %s\n",
841 : GNUNET_TIME_absolute2s (i->due));
842 : }
843 0 : break;
844 0 : case MHD_HTTP_ACCEPTED:
845 : /* KYC required, due immediately */
846 0 : break;
847 0 : case MHD_HTTP_NO_CONTENT:
848 : /* KYC is OFF, only check again if triggered */
849 0 : if (GNUNET_YES != test_mode)
850 : {
851 0 : i->due = GNUNET_TIME_relative_to_absolute (
852 : GNUNET_TIME_randomize (aml_low_freq));
853 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
854 : "KYC was disabled, randomizing inquiry to start at %s\n",
855 : GNUNET_TIME_absolute2s (i->due));
856 : }
857 0 : break;
858 0 : case MHD_HTTP_FORBIDDEN: /* bad signature */
859 : case MHD_HTTP_NOT_FOUND: /* account unknown */
860 : case MHD_HTTP_CONFLICT: /* no account_pub known */
861 : /* go immediately into long-polling */
862 0 : break;
863 13 : default:
864 : /* start with decent back-off after hard failure */
865 13 : if (GNUNET_YES != test_mode)
866 : {
867 : i->backoff
868 13 : = GNUNET_TIME_randomize (GNUNET_TIME_UNIT_MINUTES);
869 13 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
870 : "Last KYC check failed, starting with backoff %s\n",
871 : GNUNET_TIME_relative2s (i->backoff,
872 : true));
873 : }
874 13 : break;
875 : }
876 13 : inquiry_work (i);
877 : }
878 :
879 :
880 : /**
881 : * Stop KYC inquiry @a i.
882 : *
883 : * @param[in] i the inquiry to stop
884 : */
885 : static void
886 13 : stop_inquiry (struct Inquiry *i)
887 : {
888 13 : struct Account *a = i->a;
889 :
890 13 : GNUNET_CONTAINER_DLL_remove (a->i_head,
891 : a->i_tail,
892 : i);
893 13 : if (NULL != i->task)
894 : {
895 13 : GNUNET_SCHEDULER_cancel (i->task);
896 13 : i->task = NULL;
897 : }
898 13 : if (NULL != i->kyc)
899 : {
900 0 : TALER_EXCHANGE_kyc_check_cancel (i->kyc);
901 0 : i->kyc = NULL;
902 : }
903 13 : if (NULL != i->jlimits)
904 : {
905 1 : json_decref (i->jlimits);
906 1 : i->jlimits = NULL;
907 : }
908 13 : GNUNET_free (i);
909 13 : }
910 :
911 :
912 : /**
913 : * Stop KYC inquiry for account @a at exchange @a e.
914 : *
915 : * @param e an exchange
916 : * @param a an account
917 : */
918 : static void
919 0 : stop_inquiry_at (struct Exchange *e,
920 : struct Account *a)
921 : {
922 0 : for (struct Inquiry *i = a->i_head;
923 0 : NULL != i;
924 0 : i = i->next)
925 : {
926 0 : if (e == i->e)
927 : {
928 0 : stop_inquiry (i);
929 0 : return;
930 : }
931 : }
932 : /* strange, there should have been a match! */
933 0 : GNUNET_break (0);
934 : }
935 :
936 :
937 : /**
938 : * Start inquries for all exchanges on account @a a.
939 : *
940 : * @param a an account
941 : */
942 : static void
943 15 : start_inquiries (struct Account *a)
944 : {
945 15 : for (struct Exchange *e = e_head;
946 25 : NULL != e;
947 10 : e = e->next)
948 : {
949 10 : if (is_eligible (e->keys,
950 : a))
951 9 : start_inquiry (e,
952 : a);
953 : else
954 1 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
955 : "Account %s not eligible at exchange %s\n",
956 : a->merchant_account_uri.full_payto,
957 : e->keys->exchange_url);
958 : }
959 15 : }
960 :
961 :
962 : /**
963 : * Stop all inquries involving account @a a.
964 : *
965 : * @param a an account
966 : */
967 : static void
968 20 : stop_inquiries (struct Account *a)
969 : {
970 : struct Inquiry *i;
971 :
972 33 : while (NULL != (i = a->i_head))
973 13 : stop_inquiry (i);
974 20 : }
975 :
976 :
977 : /**
978 : * Callback invoked with information about a bank account.
979 : *
980 : * @param cls closure
981 : * @param merchant_priv private key of the merchant instance
982 : * @param ad details about the account
983 : */
984 : static void
985 40 : account_cb (
986 : void *cls,
987 : const struct TALER_MerchantPrivateKeyP *merchant_priv,
988 : const struct TALER_MERCHANTDB_AccountDetails *ad)
989 : {
990 40 : struct TALER_FullPayto payto_uri = ad->payto_uri;
991 :
992 40 : if (! ad->active)
993 25 : return;
994 39 : if (NULL == merchant_priv)
995 0 : return; /* instance was deleted */
996 39 : for (struct Account *a = a_head;
997 46 : NULL != a;
998 7 : a = a->next)
999 : {
1000 31 : if (0 ==
1001 31 : TALER_full_payto_cmp (payto_uri,
1002 : a->merchant_account_uri))
1003 : {
1004 24 : a->account_gen = database_gen;
1005 24 : return;
1006 : }
1007 : }
1008 : {
1009 15 : struct Account *a = GNUNET_new (struct Account);
1010 :
1011 15 : a->account_gen = database_gen;
1012 : a->merchant_account_uri.full_payto
1013 15 : = GNUNET_strdup (ad->payto_uri.full_payto);
1014 : a->instance_id
1015 15 : = GNUNET_strdup (ad->instance_id);
1016 : a->h_wire
1017 15 : = ad->h_wire;
1018 : a->ap.merchant_priv
1019 15 : = *merchant_priv;
1020 15 : TALER_full_payto_normalize_and_hash (a->merchant_account_uri,
1021 : &a->h_payto);
1022 15 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1023 : "Found account %s of instance %s with H_PAYTO %s\n",
1024 : ad->payto_uri.full_payto,
1025 : ad->instance_id,
1026 : GNUNET_sh2s (&a->h_payto.hash));
1027 15 : GNUNET_CONTAINER_DLL_insert (a_head,
1028 : a_tail,
1029 : a);
1030 15 : start_inquiries (a);
1031 : }
1032 : }
1033 :
1034 :
1035 : /**
1036 : * The set of bank accounts has changed, update our
1037 : * list of active inquiries.
1038 : *
1039 : * @param cls unused
1040 : */
1041 : static void
1042 53 : find_accounts (void *cls)
1043 : {
1044 : enum GNUNET_DB_QueryStatus qs;
1045 :
1046 : (void) cls;
1047 53 : account_task = NULL;
1048 53 : database_gen++;
1049 53 : qs = db_plugin->select_accounts (db_plugin->cls,
1050 : NULL, /* all instances */
1051 : &account_cb,
1052 : NULL);
1053 53 : if (qs < 0)
1054 : {
1055 0 : GNUNET_break (0);
1056 0 : return;
1057 : }
1058 53 : for (struct Account *a = a_head;
1059 95 : NULL != a;
1060 42 : a = a->next)
1061 : {
1062 42 : if (a->account_gen < database_gen)
1063 5 : stop_inquiries (a);
1064 : }
1065 : }
1066 :
1067 :
1068 : /**
1069 : * Function called when transfers are added to the merchant database. We look
1070 : * for more work.
1071 : *
1072 : * @param cls closure (NULL)
1073 : * @param extra additional event data provided
1074 : * @param extra_size number of bytes in @a extra
1075 : */
1076 : static void
1077 39 : account_changed (void *cls,
1078 : const void *extra,
1079 : size_t extra_size)
1080 : {
1081 : (void) cls;
1082 : (void) extra;
1083 : (void) extra_size;
1084 39 : if (NULL != account_task)
1085 0 : return;
1086 : account_task
1087 39 : = GNUNET_SCHEDULER_add_now (&find_accounts,
1088 : NULL);
1089 : }
1090 :
1091 :
1092 : /**
1093 : * Interact with the database to get the current set
1094 : * of exchange keys known to us.
1095 : *
1096 : * @param exchange_url the exchange URL to check
1097 : */
1098 : static void
1099 24 : find_keys (const char *exchange_url)
1100 : {
1101 : enum GNUNET_DB_QueryStatus qs;
1102 : struct TALER_EXCHANGE_Keys *keys;
1103 : struct Exchange *e;
1104 : struct GNUNET_TIME_Absolute first_retry;
1105 :
1106 24 : qs = db_plugin->select_exchange_keys (db_plugin->cls,
1107 : exchange_url,
1108 : &first_retry,
1109 : &keys);
1110 24 : if (qs < 0)
1111 : {
1112 0 : GNUNET_break (0);
1113 14 : return;
1114 : }
1115 24 : if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
1116 : {
1117 14 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1118 : "No %s/keys yet!\n",
1119 : exchange_url);
1120 14 : return;
1121 : }
1122 10 : for (e = e_head; NULL != e; e = e->next)
1123 : {
1124 0 : if (0 == strcmp (e->keys->exchange_url,
1125 0 : keys->exchange_url))
1126 : {
1127 0 : struct TALER_EXCHANGE_Keys *old_keys = e->keys;
1128 :
1129 0 : e->keys = keys;
1130 0 : for (struct Account *a = a_head;
1131 0 : NULL != a;
1132 0 : a = a->next)
1133 : {
1134 0 : bool was_eligible = is_eligible (old_keys,
1135 : a);
1136 0 : bool now_eligible = is_eligible (keys,
1137 : a);
1138 :
1139 0 : if (was_eligible == now_eligible)
1140 0 : continue; /* no change, do nothing */
1141 0 : if (was_eligible)
1142 0 : stop_inquiry_at (e,
1143 : a);
1144 : else /* is_eligible */
1145 0 : start_inquiry (e,
1146 : a);
1147 : }
1148 0 : TALER_EXCHANGE_keys_decref (old_keys);
1149 0 : return;
1150 : }
1151 : }
1152 10 : e = GNUNET_new (struct Exchange);
1153 10 : e->keys = keys;
1154 10 : GNUNET_CONTAINER_DLL_insert (e_head,
1155 : e_tail,
1156 : e);
1157 10 : for (struct Account *a = a_head;
1158 14 : NULL != a;
1159 4 : a = a->next)
1160 : {
1161 8 : if ( (a->account_gen == database_gen) &&
1162 4 : (is_eligible (e->keys,
1163 : a)) )
1164 4 : start_inquiry (e,
1165 : a);
1166 : }
1167 : }
1168 :
1169 :
1170 : /**
1171 : * Function called when keys were changed in the
1172 : * merchant database. Updates ours.
1173 : *
1174 : * @param cls closure (NULL)
1175 : * @param extra additional event data provided
1176 : * @param extra_size number of bytes in @a extra
1177 : */
1178 : static void
1179 10 : keys_changed (void *cls,
1180 : const void *extra,
1181 : size_t extra_size)
1182 : {
1183 10 : const char *url = extra;
1184 :
1185 : (void) cls;
1186 10 : if ( (NULL == extra) ||
1187 : (0 == extra_size) )
1188 : {
1189 0 : GNUNET_break (0);
1190 0 : return;
1191 : }
1192 10 : if ('\0' != url[extra_size - 1])
1193 : {
1194 0 : GNUNET_break (0);
1195 0 : return;
1196 : }
1197 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1198 : "Received keys change notification: reload `%s'\n",
1199 : url);
1200 10 : find_keys (url);
1201 : }
1202 :
1203 :
1204 : /**
1205 : * Function called when a KYC rule was triggered by
1206 : * a transaction and we need to get the latest KYC
1207 : * status immediately.
1208 : *
1209 : * @param cls closure (NULL)
1210 : * @param extra additional event data provided
1211 : * @param extra_size number of bytes in @a extra
1212 : */
1213 : static void
1214 0 : rule_triggered (void *cls,
1215 : const void *extra,
1216 : size_t extra_size)
1217 : {
1218 0 : const char *text = extra;
1219 : const char *space;
1220 : struct TALER_MerchantWireHashP h_wire;
1221 : const char *exchange_url;
1222 :
1223 : (void) cls;
1224 0 : if ( (NULL == extra) ||
1225 : (0 == extra_size) )
1226 : {
1227 0 : GNUNET_break (0);
1228 0 : return;
1229 : }
1230 0 : if ('\0' != text[extra_size - 1])
1231 : {
1232 0 : GNUNET_break (0);
1233 0 : return;
1234 : }
1235 0 : space = memchr (extra,
1236 : ' ',
1237 : extra_size);
1238 0 : if (NULL == space)
1239 : {
1240 0 : GNUNET_break (0);
1241 0 : return;
1242 : }
1243 0 : if (GNUNET_OK !=
1244 0 : GNUNET_STRINGS_string_to_data (extra,
1245 0 : space - text,
1246 : &h_wire,
1247 : sizeof (h_wire)))
1248 : {
1249 0 : GNUNET_break (0);
1250 0 : return;
1251 : }
1252 0 : exchange_url = &space[1];
1253 0 : if (! TALER_is_web_url (exchange_url))
1254 : {
1255 0 : GNUNET_break (0);
1256 0 : return;
1257 : }
1258 :
1259 0 : for (struct Account *a = a_head;
1260 0 : NULL != a;
1261 0 : a = a->next)
1262 : {
1263 0 : if (0 !=
1264 0 : GNUNET_memcmp (&h_wire,
1265 : &a->h_wire))
1266 0 : continue;
1267 0 : for (struct Inquiry *i = a->i_head;
1268 0 : NULL != i;
1269 0 : i = i->next)
1270 : {
1271 0 : if (0 != strcmp (exchange_url,
1272 0 : i->e->keys->exchange_url))
1273 0 : continue;
1274 0 : i->kyc_ok = false;
1275 0 : i->due = GNUNET_TIME_UNIT_ZERO_ABS;
1276 0 : if (NULL != i->task)
1277 : {
1278 0 : GNUNET_SCHEDULER_cancel (i->task);
1279 0 : i->task = NULL;
1280 : }
1281 0 : if (NULL != i->kyc)
1282 : {
1283 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1284 : "/kyc-check already running for %s\n",
1285 : text);
1286 0 : return;
1287 : }
1288 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1289 : "Starting %skyc-check for `%s' due to KYC rule trigger\n",
1290 : exchange_url,
1291 : i->a->merchant_account_uri.full_payto);
1292 0 : i->task = GNUNET_SCHEDULER_add_at (i->due,
1293 : &inquiry_work,
1294 : i);
1295 0 : return;
1296 : }
1297 : }
1298 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1299 : "KYC rule trigger notification `%s' matches none of our accounts\n",
1300 : text);
1301 : }
1302 :
1303 :
1304 : /**
1305 : * Function called on each configuration section. Finds sections
1306 : * about exchanges, parses the entries.
1307 : *
1308 : * @param cls NULL
1309 : * @param section name of the section
1310 : */
1311 : static void
1312 564 : accept_exchanges (void *cls,
1313 : const char *section)
1314 : {
1315 : char *url;
1316 :
1317 : (void) cls;
1318 564 : if (0 !=
1319 564 : strncasecmp (section,
1320 : "merchant-exchange-",
1321 : strlen ("merchant-exchange-")))
1322 550 : return;
1323 42 : if (GNUNET_YES ==
1324 42 : GNUNET_CONFIGURATION_get_value_yesno (cfg,
1325 : section,
1326 : "DISABLED"))
1327 28 : return;
1328 14 : if (GNUNET_OK !=
1329 14 : GNUNET_CONFIGURATION_get_value_string (cfg,
1330 : section,
1331 : "EXCHANGE_BASE_URL",
1332 : &url))
1333 : {
1334 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
1335 : section,
1336 : "EXCHANGE_BASE_URL");
1337 0 : global_ret = EXIT_NOTCONFIGURED;
1338 0 : GNUNET_SCHEDULER_shutdown ();
1339 0 : return;
1340 : }
1341 14 : find_keys (url);
1342 14 : GNUNET_free (url);
1343 : }
1344 :
1345 :
1346 : /**
1347 : * We're being aborted with CTRL-C (or SIGTERM). Shut down.
1348 : *
1349 : * @param cls closure (NULL)
1350 : */
1351 : static void
1352 14 : shutdown_task (void *cls)
1353 : {
1354 : (void) cls;
1355 14 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1356 : "Running shutdown\n");
1357 24 : while (NULL != e_head)
1358 : {
1359 10 : struct Exchange *e = e_head;
1360 :
1361 10 : if (NULL != e->keys)
1362 : {
1363 10 : TALER_EXCHANGE_keys_decref (e->keys);
1364 10 : e->keys = NULL;
1365 : }
1366 10 : GNUNET_CONTAINER_DLL_remove (e_head,
1367 : e_tail,
1368 : e);
1369 10 : GNUNET_free (e);
1370 : }
1371 29 : while (NULL != a_head)
1372 : {
1373 15 : struct Account *a = a_head;
1374 :
1375 15 : stop_inquiries (a);
1376 15 : GNUNET_CONTAINER_DLL_remove (a_head,
1377 : a_tail,
1378 : a);
1379 15 : GNUNET_free (a->merchant_account_uri.full_payto);
1380 15 : GNUNET_free (a->instance_id);
1381 15 : GNUNET_free (a);
1382 : }
1383 14 : if (NULL != eh_accounts)
1384 : {
1385 14 : db_plugin->event_listen_cancel (eh_accounts);
1386 14 : eh_accounts = NULL;
1387 : }
1388 14 : if (NULL != account_task)
1389 : {
1390 0 : GNUNET_SCHEDULER_cancel (account_task);
1391 0 : account_task = NULL;
1392 : }
1393 14 : if (NULL != eh_keys)
1394 : {
1395 14 : db_plugin->event_listen_cancel (eh_keys);
1396 14 : eh_keys = NULL;
1397 : }
1398 14 : if (NULL != eh_rule)
1399 : {
1400 14 : db_plugin->event_listen_cancel (eh_rule);
1401 14 : eh_rule = NULL;
1402 : }
1403 14 : TALER_MERCHANTDB_plugin_unload (db_plugin);
1404 14 : db_plugin = NULL;
1405 14 : cfg = NULL;
1406 14 : if (NULL != ctx)
1407 : {
1408 14 : GNUNET_CURL_fini (ctx);
1409 14 : ctx = NULL;
1410 : }
1411 14 : if (NULL != rc)
1412 : {
1413 14 : GNUNET_CURL_gnunet_rc_destroy (rc);
1414 14 : rc = NULL;
1415 : }
1416 14 : }
1417 :
1418 :
1419 : /**
1420 : * First task.
1421 : *
1422 : * @param cls closure, NULL
1423 : * @param args remaining command-line arguments
1424 : * @param cfgfile name of the configuration file used (for saving, can be NULL!)
1425 : * @param c configuration
1426 : */
1427 : static void
1428 14 : run (void *cls,
1429 : char *const *args,
1430 : const char *cfgfile,
1431 : const struct GNUNET_CONFIGURATION_Handle *c)
1432 : {
1433 : (void) args;
1434 : (void) cfgfile;
1435 :
1436 14 : cfg = c;
1437 14 : if (GNUNET_OK !=
1438 14 : GNUNET_CONFIGURATION_get_value_time (cfg,
1439 : "merchant-kyccheck",
1440 : "AML_FREQ",
1441 : &aml_freq))
1442 : {
1443 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1444 : "merchant-kyccheck",
1445 : "AML_FREQ");
1446 : /* use default */
1447 0 : aml_freq = AML_FREQ;
1448 : }
1449 14 : if (GNUNET_OK !=
1450 14 : GNUNET_CONFIGURATION_get_value_time (cfg,
1451 : "merchant-kyccheck",
1452 : "AML_LOW_FREQ",
1453 : &aml_low_freq))
1454 : {
1455 0 : GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
1456 : "merchant-kyccheck",
1457 : "AML_LOW_FREQ");
1458 : /* use default */
1459 0 : aml_low_freq = AML_LOW_FREQ;
1460 : }
1461 14 : if (GNUNET_TIME_relative_cmp (aml_low_freq,
1462 : <,
1463 : aml_freq))
1464 : {
1465 0 : aml_low_freq = GNUNET_TIME_relative_multiply (aml_freq,
1466 : 10);
1467 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1468 : "AML_LOW_FREQ was set to less than AML_FREQ. Using %s instead\n",
1469 : GNUNET_TIME_relative2s (aml_low_freq,
1470 : true));
1471 : }
1472 14 : GNUNET_SCHEDULER_add_shutdown (&shutdown_task,
1473 : NULL);
1474 14 : ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
1475 : &rc);
1476 14 : rc = GNUNET_CURL_gnunet_rc_create (ctx);
1477 14 : if (NULL == ctx)
1478 : {
1479 0 : GNUNET_break (0);
1480 0 : GNUNET_SCHEDULER_shutdown ();
1481 0 : global_ret = EXIT_FAILURE;
1482 0 : return;
1483 : }
1484 14 : if (NULL ==
1485 14 : (db_plugin = TALER_MERCHANTDB_plugin_load (cfg)))
1486 : {
1487 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1488 : "Failed to initialize DB subsystem\n");
1489 0 : GNUNET_SCHEDULER_shutdown ();
1490 0 : global_ret = EXIT_NOTCONFIGURED;
1491 0 : return;
1492 : }
1493 14 : if (GNUNET_OK !=
1494 14 : db_plugin->connect (db_plugin->cls))
1495 : {
1496 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1497 : "Failed to connect to database. Consider running taler-merchant-dbinit.\n");
1498 0 : GNUNET_SCHEDULER_shutdown ();
1499 0 : global_ret = EXIT_FAILURE;
1500 0 : return;
1501 : }
1502 : {
1503 14 : struct GNUNET_DB_EventHeaderP es = {
1504 14 : .size = htons (sizeof (es)),
1505 14 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KEYS)
1506 : };
1507 :
1508 : eh_keys
1509 28 : = db_plugin->event_listen (db_plugin->cls,
1510 : &es,
1511 14 : GNUNET_TIME_UNIT_FOREVER_REL,
1512 : &keys_changed,
1513 : NULL);
1514 : }
1515 : {
1516 14 : struct GNUNET_DB_EventHeaderP es = {
1517 14 : .size = htons (sizeof (es)),
1518 14 : .type = htons (TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_RULE_TRIGGERED)
1519 : };
1520 :
1521 : eh_rule
1522 28 : = db_plugin->event_listen (db_plugin->cls,
1523 : &es,
1524 14 : GNUNET_TIME_UNIT_FOREVER_REL,
1525 : &rule_triggered,
1526 : NULL);
1527 : }
1528 14 : GNUNET_CONFIGURATION_iterate_sections (cfg,
1529 : &accept_exchanges,
1530 : NULL);
1531 : {
1532 14 : struct GNUNET_DB_EventHeaderP es = {
1533 14 : .size = htons (sizeof (es)),
1534 14 : .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED)
1535 : };
1536 :
1537 : eh_accounts
1538 28 : = db_plugin->event_listen (db_plugin->cls,
1539 : &es,
1540 14 : GNUNET_TIME_UNIT_FOREVER_REL,
1541 : &account_changed,
1542 : NULL);
1543 : }
1544 14 : GNUNET_assert (NULL == account_task);
1545 : account_task
1546 14 : = GNUNET_SCHEDULER_add_now (&find_accounts,
1547 : NULL);
1548 : }
1549 :
1550 :
1551 : /**
1552 : * The main function of taler-merchant-kyccheck
1553 : *
1554 : * @param argc number of arguments from the command line
1555 : * @param argv command line arguments
1556 : * @return 0 ok, 1 on error
1557 : */
1558 : int
1559 14 : main (int argc,
1560 : char *const *argv)
1561 : {
1562 14 : struct GNUNET_GETOPT_CommandLineOption options[] = {
1563 14 : GNUNET_GETOPT_option_timetravel ('T',
1564 : "timetravel"),
1565 14 : GNUNET_GETOPT_option_flag ('t',
1566 : "test",
1567 : "run in test mode and exit when idle",
1568 : &test_mode),
1569 14 : GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION),
1570 : GNUNET_GETOPT_OPTION_END
1571 : };
1572 : enum GNUNET_GenericReturnValue ret;
1573 :
1574 14 : ret = GNUNET_PROGRAM_run (
1575 : TALER_MERCHANT_project_data (),
1576 : argc, argv,
1577 : "taler-merchant-kyccheck",
1578 : gettext_noop (
1579 : "background process that checks the KYC state of our bank accounts at various exchanges"),
1580 : options,
1581 : &run, NULL);
1582 14 : if (GNUNET_SYSERR == ret)
1583 0 : return EXIT_INVALIDARGUMENT;
1584 14 : if (GNUNET_NO == ret)
1585 0 : return EXIT_SUCCESS;
1586 14 : return global_ret;
1587 : }
1588 :
1589 :
1590 : /* end of taler-merchant-kyccheck.c */
|