Line data Source code
1 : /*
2 : This file is part of GNU Taler
3 : (C) 2021-2026 Taler Systems SA
4 :
5 : GNU Taler is free software; you can redistribute it and/or modify
6 : it under the terms of the GNU Affero General Public License as
7 : published by the Free Software Foundation; either version 3,
8 : or (at your option) any later version.
9 :
10 : GNU Taler is distributed in the hope that it will be useful, but
11 : WITHOUT ANY WARRANTY; without even the implied warranty of
12 : MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 : GNU General Public License for more details.
14 :
15 : You should have received a copy of the GNU General Public
16 : License along with TALER; see the file COPYING. If not,
17 : see <http://www.gnu.org/licenses/>
18 : */
19 :
20 : /**
21 : * @file src/backend/taler-merchant-httpd_get-private-kyc.c
22 : * @brief implementing GET /instances/$ID/kyc request handling
23 : * @author Christian Grothoff
24 : */
25 : #include "platform.h"
26 : #include "taler-merchant-httpd_exchanges.h"
27 : #include "taler-merchant-httpd_get-private-kyc.h"
28 : #include "taler-merchant-httpd_kyc-order.h"
29 : #include "taler-merchant-httpd_helper.h"
30 : #include "taler-merchant-httpd_get-exchanges.h"
31 : #include <taler/taler_json_lib.h>
32 : #include <taler/taler_templating_lib.h>
33 : #include <taler/taler_dbevents.h>
34 : #include <regex.h>
35 : #include "merchant-database/iterate_kyc_statuses.h"
36 : #include "merchant-database/event_listen.h"
37 : #include "merchant-database/set_instance.h"
38 : #include "merchant-database/get_tos_accepted_early.h"
39 :
40 : /**
41 : * Information we keep per /kyc request.
42 : */
43 : struct KycContext;
44 :
45 :
46 : /**
47 : * Structure for tracking requests to the exchange's
48 : * ``/kyc-check`` API.
49 : */
50 : struct ExchangeKycRequest
51 : {
52 : /**
53 : * Kept in a DLL.
54 : */
55 : struct ExchangeKycRequest *next;
56 :
57 : /**
58 : * Kept in a DLL.
59 : */
60 : struct ExchangeKycRequest *prev;
61 :
62 : /**
63 : * Find operation where we connect to the respective exchange.
64 : */
65 : struct TMH_EXCHANGES_KeysOperation *fo;
66 :
67 : /**
68 : * JSON array of payto-URIs with KYC auth wire transfer
69 : * instructions. Provided if @e auth_ok is false and
70 : * @e kyc_auth_conflict is false.
71 : */
72 : json_t *pkaa;
73 :
74 : /**
75 : * The keys of the exchange.
76 : */
77 : struct TALER_EXCHANGE_Keys *keys;
78 :
79 : /**
80 : * KYC request this exchange request is made for.
81 : */
82 : struct KycContext *kc;
83 :
84 : /**
85 : * JSON array of AccountLimits that apply, NULL if
86 : * unknown (and likely defaults apply).
87 : */
88 : json_t *jlimits;
89 :
90 : /**
91 : * Our account's payto URI.
92 : */
93 : struct TALER_FullPayto payto_uri;
94 :
95 : /**
96 : * Base URL of the exchange.
97 : */
98 : char *exchange_url;
99 :
100 : /**
101 : * Hash of the wire account (with salt) we are checking.
102 : */
103 : struct TALER_MerchantWireHashP h_wire;
104 :
105 : /**
106 : * Current access token for the KYC SPA. Only set
107 : * if @e auth_ok is true.
108 : */
109 : struct TALER_AccountAccessTokenP access_token;
110 :
111 : /**
112 : * Timestamp when we last got a reply from the exchange.
113 : */
114 : struct GNUNET_TIME_Timestamp last_check;
115 :
116 : /**
117 : * Last HTTP status code obtained via /kyc-check from the exchange.
118 : */
119 : unsigned int last_http_status;
120 :
121 : /**
122 : * Last Taler error code returned from /kyc-check.
123 : */
124 : enum TALER_ErrorCode last_ec;
125 :
126 : /**
127 : * True if this account cannot work at this exchange because KYC auth is
128 : * impossible.
129 : */
130 : bool kyc_auth_conflict;
131 :
132 : /**
133 : * We could not get /keys from the exchange.
134 : */
135 : bool no_keys;
136 :
137 : /**
138 : * True if @e access_token is available.
139 : */
140 : bool auth_ok;
141 :
142 : /**
143 : * True if we believe no KYC is currently required
144 : * for this account at this exchange.
145 : */
146 : bool kyc_ok;
147 :
148 : /**
149 : * True if the exchange exposed to us that the account
150 : * is currently under AML review.
151 : */
152 : bool in_aml_review;
153 :
154 : };
155 :
156 :
157 : /**
158 : * Information we keep per /kyc request.
159 : */
160 : struct KycContext
161 : {
162 : /**
163 : * Stored in a DLL.
164 : */
165 : struct KycContext *next;
166 :
167 : /**
168 : * Stored in a DLL.
169 : */
170 : struct KycContext *prev;
171 :
172 : /**
173 : * Connection we are handling.
174 : */
175 : struct MHD_Connection *connection;
176 :
177 : /**
178 : * Instance we are serving.
179 : */
180 : struct TMH_MerchantInstance *mi;
181 :
182 : /**
183 : * Our handler context.
184 : */
185 : struct TMH_HandlerContext *hc;
186 :
187 : /**
188 : * JSON array where we are building up the array with
189 : * pending KYC operations.
190 : */
191 : json_t *kycs_data;
192 :
193 : /**
194 : * Head of DLL of requests we are making to an
195 : * exchange to inquire about the latest KYC status.
196 : */
197 : struct ExchangeKycRequest *exchange_pending_head;
198 :
199 : /**
200 : * Tail of DLL of requests we are making to an
201 : * exchange to inquire about the latest KYC status.
202 : */
203 : struct ExchangeKycRequest *exchange_pending_tail;
204 :
205 : /**
206 : * Notification handler from database on changes
207 : * to the KYC status.
208 : */
209 : struct GNUNET_DB_EventHandler *eh;
210 :
211 : /**
212 : * Set to the exchange URL, or NULL to not filter by
213 : * exchange. "exchange_url" query parameter.
214 : */
215 : const char *exchange_url;
216 :
217 : /**
218 : * How long are we willing to wait for the exchange(s)?
219 : * Based on "timeout_ms" query parameter.
220 : */
221 : struct GNUNET_TIME_Absolute timeout;
222 :
223 : /**
224 : * Set to the h_wire of the merchant account if
225 : * @a have_h_wire is true, used to filter by account.
226 : * Set from "h_wire" query parameter.
227 : */
228 : struct TALER_MerchantWireHashP h_wire;
229 :
230 : /**
231 : * Set to the Etag of a response already known to the
232 : * client. We should only return from long-polling
233 : * on timeout (with "Not Modified") or when the Etag
234 : * of the response differs from what is given here.
235 : * Only set if @a have_lp_not_etag is true.
236 : * Set from "lp_etag" query parameter.
237 : */
238 : struct GNUNET_ShortHashCode lp_not_etag;
239 :
240 : /**
241 : * Specifies what status change we are long-polling for. If specified, the
242 : * endpoint will only return once the status *matches* the given value. If
243 : * multiple accounts or exchanges match the query, any account reaching the
244 : * STATUS will cause the response to be returned.
245 : */
246 : const char *lp_status;
247 :
248 : /**
249 : * Specifies what status change we are long-polling for. If specified, the
250 : * endpoint will only return once the status no longer matches the given
251 : * value. If multiple accounts or exchanges *no longer matches* the given
252 : * STATUS will cause the response to be returned.
253 : */
254 : const char *lp_not_status;
255 :
256 : /**
257 : * #GNUNET_NO if the @e connection was not suspended,
258 : * #GNUNET_YES if the @e connection was suspended,
259 : * #GNUNET_SYSERR if @e connection was resumed to as
260 : * part of #MH_force_pc_resume during shutdown.
261 : */
262 : enum GNUNET_GenericReturnValue suspended;
263 :
264 : /**
265 : * What state are we long-polling for? "lpt" argument.
266 : */
267 : enum TALER_EXCHANGE_KycLongPollTarget lpt;
268 :
269 : /**
270 : * Processing phase.
271 : */
272 : enum
273 : {
274 : PHASE_INIT = 0,
275 : PHASE_DETERMINE_LONG_POLL,
276 : PHASE_DATABASE_KYC_CHECK,
277 : PHASE_NO_ACCOUNTS,
278 : PHASE_GENERATE_RESPONSE,
279 : PHASE_IN_SHUTDOWN = 999,
280 : PHASE_RETURN_YES,
281 : PHASE_RETURN_NO,
282 : PHASE_SUSPENDED_ON_ACCOUNT,
283 : PHASE_SUSPENDED_ON_EXCHANGE,
284 : } phase;
285 :
286 : /**
287 : * Output format requested by the client.
288 : */
289 : enum
290 : {
291 : POF_JSON,
292 : POF_TEXT,
293 : POF_PDF
294 : } format;
295 :
296 : /**
297 : * Set to true if the database notified us about a change
298 : * in the account but we did not yet check the database
299 : * status as we were waiting on something else.
300 : */
301 : bool account_signal;
302 :
303 : /**
304 : * True if @e h_wire was given.
305 : */
306 : bool have_h_wire;
307 :
308 : /**
309 : * True if @e lp_not_etag was given.
310 : */
311 : bool have_lp_not_etag;
312 :
313 : /**
314 : * We're still waiting on the exchange to determine
315 : * the KYC status of our deposit(s).
316 : */
317 : bool return_immediately;
318 :
319 : /**
320 : * Are we currently still iterating over the database and
321 : * thus must not yet respond?
322 : */
323 : bool in_db;
324 :
325 : /**
326 : * The initial database read already requested an exchange refresh.
327 : */
328 : bool refresh_requested;
329 : };
330 :
331 :
332 : /**
333 : * Head of DLL.
334 : */
335 : static struct KycContext *kc_head;
336 :
337 : /**
338 : * Tail of DLL.
339 : */
340 : static struct KycContext *kc_tail;
341 :
342 :
343 : /* ******************* cleanup ***************** */
344 :
345 : void
346 20 : TMH_force_kyc_resume ()
347 : {
348 20 : for (struct KycContext *kc = kc_head;
349 20 : NULL != kc;
350 0 : kc = kc->next)
351 : {
352 0 : if (GNUNET_YES == kc->suspended)
353 : {
354 0 : kc->suspended = GNUNET_SYSERR;
355 0 : kc->phase = PHASE_IN_SHUTDOWN;
356 0 : MHD_resume_connection (kc->connection);
357 : }
358 : }
359 20 : }
360 :
361 :
362 : /**
363 : * Release resources of @a ekr
364 : *
365 : * @param[in] ekr key request data to clean up
366 : */
367 : static void
368 10 : ekr_cleanup (struct ExchangeKycRequest *ekr)
369 : {
370 10 : struct KycContext *kc = ekr->kc;
371 :
372 10 : GNUNET_CONTAINER_DLL_remove (kc->exchange_pending_head,
373 : kc->exchange_pending_tail,
374 : ekr);
375 10 : if (NULL != ekr->fo)
376 : {
377 0 : TMH_EXCHANGES_keys4exchange_cancel (ekr->fo);
378 0 : ekr->fo = NULL;
379 : }
380 10 : json_decref (ekr->pkaa);
381 10 : json_decref (ekr->jlimits);
382 10 : if (NULL != ekr->keys)
383 6 : TALER_EXCHANGE_keys_decref (ekr->keys);
384 10 : GNUNET_free (ekr->exchange_url);
385 10 : GNUNET_free (ekr->payto_uri.full_payto);
386 10 : GNUNET_free (ekr);
387 10 : }
388 :
389 :
390 : /**
391 : * Custom cleanup routine for a `struct KycContext`.
392 : *
393 : * @param cls the `struct KycContext` to clean up.
394 : */
395 : static void
396 12 : kyc_context_cleanup (void *cls)
397 : {
398 12 : struct KycContext *kc = cls;
399 : struct ExchangeKycRequest *ekr;
400 :
401 12 : while (NULL != (ekr = kc->exchange_pending_head))
402 : {
403 0 : ekr_cleanup (ekr);
404 : }
405 12 : if (NULL != kc->eh)
406 : {
407 4 : TALER_MERCHANTDB_event_listen_cancel (kc->eh);
408 4 : kc->eh = NULL;
409 : }
410 12 : GNUNET_CONTAINER_DLL_remove (kc_head,
411 : kc_tail,
412 : kc);
413 12 : json_decref (kc->kycs_data);
414 12 : GNUNET_free (kc);
415 12 : }
416 :
417 :
418 : /**
419 : * Finish handling the connection returning @a ret to MHD
420 : *
421 : * @param[in,out] kc connection we are handling
422 : * @param mhd_ret result to return for the @a kc request
423 : */
424 : static void
425 12 : finish_request (struct KycContext *kc,
426 : enum MHD_Result mhd_ret)
427 : {
428 12 : kc->phase = (MHD_YES == mhd_ret)
429 : ? PHASE_RETURN_YES
430 12 : : PHASE_RETURN_NO;
431 12 : }
432 :
433 :
434 : /* ******************* phase_init ***************** */
435 :
436 :
437 : /**
438 : * Initialize basic data structures of the connection,
439 : * finishes parsing the request.
440 : *
441 : * @param[in,out] kc connection we are handling
442 : */
443 : static void
444 12 : phase_init (struct KycContext *kc)
445 : {
446 12 : kc->kycs_data = json_array ();
447 12 : GNUNET_assert (NULL != kc->kycs_data);
448 : /* process 'exchange_url' argument */
449 12 : kc->exchange_url = MHD_lookup_connection_value (
450 : kc->connection,
451 : MHD_GET_ARGUMENT_KIND,
452 : "exchange_url");
453 12 : if ( (NULL != kc->exchange_url) &&
454 9 : ( (! TALER_url_valid_charset (kc->exchange_url)) ||
455 9 : (! TALER_is_web_url (kc->exchange_url)) ) )
456 : {
457 0 : GNUNET_break_op (0);
458 0 : finish_request (kc,
459 : TALER_MHD_reply_with_error (
460 : kc->connection,
461 : MHD_HTTP_BAD_REQUEST,
462 : TALER_EC_GENERIC_PARAMETER_MALFORMED,
463 : "exchange_url must be a valid HTTP(s) URL"));
464 0 : return;
465 : }
466 :
467 : /* Determine desired output format from Accept header */
468 : {
469 : const char *mime;
470 :
471 12 : mime = MHD_lookup_connection_value (kc->connection,
472 : MHD_HEADER_KIND,
473 : MHD_HTTP_HEADER_ACCEPT);
474 12 : if (NULL == mime)
475 0 : mime = "application/json";
476 12 : if (0 == strcmp (mime,
477 : "*/*"))
478 10 : mime = "application/json";
479 12 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
480 : "KYC status requested for format %s\n",
481 : mime);
482 12 : if (0 == strcmp (mime,
483 : "application/json"))
484 : {
485 11 : kc->format = POF_JSON;
486 : }
487 1 : else if (0 == strcmp (mime,
488 : "text/plain"))
489 : {
490 1 : kc->format = POF_TEXT;
491 : }
492 : #if FUTURE
493 : else if (0 == strcmp (mime,
494 : "application/pdf"))
495 : {
496 : kc->format = POF_PDF;
497 : }
498 : #endif
499 : else
500 : {
501 0 : GNUNET_break_op (0);
502 0 : finish_request (kc,
503 0 : TALER_MHD_REPLY_JSON_PACK (
504 : kc->connection,
505 : MHD_HTTP_NOT_ACCEPTABLE,
506 : GNUNET_JSON_pack_string ("hint",
507 : mime)));
508 0 : return;
509 : }
510 : }
511 12 : kc->phase++;
512 : }
513 :
514 :
515 : /* ******************* phase_determine_long_poll ***************** */
516 :
517 :
518 : /**
519 : * Handle a DB event about an update relevant
520 : * for the processing of the kyc request.
521 : *
522 : * @param cls our `struct KycContext`
523 : * @param extra additional event data provided
524 : * @param extra_size number of bytes in @a extra
525 : */
526 : static void
527 0 : kyc_change_cb (void *cls,
528 : const void *extra,
529 : size_t extra_size)
530 : {
531 0 : struct KycContext *kc = cls;
532 :
533 0 : if ( (GNUNET_YES == kc->suspended) &&
534 0 : (PHASE_SUSPENDED_ON_ACCOUNT == kc->phase) )
535 : {
536 0 : GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
537 : "Resuming KYC with gateway timeout\n");
538 0 : kc->suspended = GNUNET_NO;
539 0 : kc->phase = PHASE_DATABASE_KYC_CHECK;
540 0 : MHD_resume_connection (kc->connection);
541 0 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
542 : }
543 : else
544 : {
545 : /* remember for later */
546 0 : kc->account_signal = true;
547 : }
548 0 : }
549 :
550 :
551 : void
552 14 : TMH_kyc_keys_changed (const char *exchange_url)
553 : {
554 14 : for (struct KycContext *kc = kc_head;
555 14 : NULL != kc;
556 0 : kc = kc->next)
557 : {
558 : size_t off;
559 : const json_t *entry;
560 0 : bool affected = false;
561 :
562 0 : if (NULL == kc->eh)
563 0 : continue; /* no long poll */
564 : /* Completed account lookups live in the response; other lookups may
565 : still be waiting for keys. Check both to cover updates during reads. */
566 0 : json_array_foreach (kc->kycs_data, off, entry)
567 : {
568 0 : const char *url = json_string_value (json_object_get (entry,
569 : "exchange_url"));
570 :
571 0 : if ( (NULL != url) &&
572 0 : (0 == strcmp (url, exchange_url)) )
573 : {
574 0 : affected = true;
575 0 : break;
576 : }
577 : }
578 0 : for (struct ExchangeKycRequest *ekr = kc->exchange_pending_head;
579 0 : (! affected) && (NULL != ekr);
580 0 : ekr = ekr->next)
581 0 : affected = (0 == strcmp (ekr->exchange_url, exchange_url));
582 0 : if (affected)
583 : {
584 : /* Keys affect ToS flags, default limits and KYC auth instructions.
585 : Recompute the full response and let its ETag determine whether to
586 : return. Like a status notification, this does not force a refresh. */
587 0 : kyc_change_cb (kc, NULL, 0);
588 : }
589 : }
590 14 : }
591 :
592 :
593 : /**
594 : * Suspend @a kc until we have a change in the account status.
595 : *
596 : * @param[in,out] kc request to suspend
597 : */
598 : static void
599 0 : wait_for_account (struct KycContext *kc)
600 : {
601 0 : GNUNET_assert (GNUNET_NO == kc->suspended);
602 0 : if (kc->account_signal)
603 : {
604 : /* we got a NOTIFY earlier, handle it immediately */
605 0 : kc->account_signal = false;
606 0 : kc->phase = PHASE_DATABASE_KYC_CHECK;
607 0 : return;
608 : }
609 : /* Wait on account notification */
610 0 : MHD_suspend_connection (kc->connection);
611 0 : kc->suspended = GNUNET_YES;
612 0 : kc->phase = PHASE_SUSPENDED_ON_ACCOUNT;
613 : }
614 :
615 :
616 : /**
617 : * Setup long-polling for the connection, if applicable.
618 : *
619 : * @param[in,out] kc connection we are handling
620 : */
621 : static void
622 12 : phase_determine_long_poll (struct KycContext *kc)
623 : {
624 12 : if (GNUNET_TIME_absolute_is_past (kc->timeout))
625 : {
626 8 : kc->phase++;
627 8 : return;
628 : }
629 4 : if (kc->have_h_wire)
630 : {
631 2 : struct TALER_MERCHANTDB_MerchantKycStatusChangeEventP ev = {
632 2 : .header.size = htons (sizeof (ev)),
633 2 : .header.type = htons (
634 : TALER_DBEVENT_MERCHANT_EXCHANGE_KYC_STATUS_CHANGED
635 : ),
636 2 : .merchant_pub = kc->mi->merchant_pub,
637 : .h_wire = kc->h_wire
638 : };
639 :
640 2 : kc->eh = TALER_MERCHANTDB_event_listen (
641 : TMH_db,
642 : &ev.header,
643 : GNUNET_TIME_absolute_get_remaining (kc->timeout),
644 : &kyc_change_cb,
645 : kc);
646 : }
647 : else
648 : {
649 2 : struct TALER_MERCHANTDB_InstanceKycStatusChangeEventP hdr = {
650 2 : .header.size = htons (sizeof (hdr)),
651 2 : .header.type = htons (TALER_DBEVENT_MERCHANT_KYC_STATUS_CHANGED),
652 2 : .merchant_pub = kc->mi->merchant_pub
653 : };
654 :
655 2 : kc->eh = TALER_MERCHANTDB_event_listen (
656 : TMH_db,
657 : &hdr.header,
658 : GNUNET_TIME_absolute_get_remaining (kc->timeout),
659 : &kyc_change_cb,
660 : kc);
661 : }
662 4 : kc->phase++;
663 : }
664 :
665 :
666 : /* ***************** phase_database_kyc_check ************** */
667 :
668 :
669 : /**
670 : * Maps @a ekr to a status code for clients to interpret the
671 : * overall result.
672 : *
673 : * @param ekr request summary
674 : * @return status of the KYC state as a string
675 : */
676 : static const char *
677 10 : map_to_status (const struct ExchangeKycRequest *ekr)
678 : {
679 10 : if (ekr->no_keys)
680 : {
681 0 : return "no-exchange-keys";
682 : }
683 10 : if (TALER_EC_MERCHANT_PRIVATE_ACCOUNT_NOT_ELIGIBLE_FOR_EXCHANGE ==
684 10 : ekr->last_ec)
685 0 : return "unsupported-account";
686 10 : if (ekr->kyc_ok)
687 : {
688 9 : if (NULL != ekr->jlimits)
689 : {
690 : size_t off;
691 : json_t *limit;
692 4 : json_array_foreach (ekr->jlimits, off, limit)
693 : {
694 : struct TALER_Amount threshold;
695 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type;
696 2 : bool soft = false;
697 : struct GNUNET_JSON_Specification spec[] = {
698 2 : TALER_JSON_spec_kycte ("operation_type",
699 : &operation_type),
700 2 : TALER_JSON_spec_amount_any ("threshold",
701 : &threshold),
702 2 : GNUNET_JSON_spec_mark_optional (
703 : GNUNET_JSON_spec_bool ("soft_limit",
704 : &soft),
705 : NULL),
706 2 : GNUNET_JSON_spec_end ()
707 : };
708 :
709 2 : if (GNUNET_OK !=
710 2 : GNUNET_JSON_parse (limit,
711 : spec,
712 : NULL, NULL))
713 : {
714 0 : GNUNET_break (0);
715 2 : return "merchant-internal-error";
716 : }
717 2 : if (! TALER_amount_is_zero (&threshold))
718 0 : continue; /* only care about zero-limits */
719 2 : if (! soft)
720 0 : continue; /* only care about soft limits */
721 2 : if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
722 2 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
723 0 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
724 : {
725 2 : if (! ekr->auth_ok)
726 : {
727 0 : if (ekr->kyc_auth_conflict)
728 0 : return "kyc-wire-impossible";
729 0 : return "kyc-wire-required";
730 : }
731 2 : return "kyc-required";
732 : }
733 : }
734 : }
735 7 : if (NULL == ekr->jlimits)
736 : {
737 : /* check default limits */
738 5 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
739 :
740 5 : for (unsigned int i = 0; i < keys->zero_limits_length; i++)
741 : {
742 0 : enum TALER_KYCLOGIC_KycTriggerEvent operation_type
743 0 : = keys->zero_limits[i].operation_type;
744 :
745 0 : if ( (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_DEPOSIT) ||
746 0 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_AGGREGATE) ||
747 : (operation_type == TALER_KYCLOGIC_KYC_TRIGGER_TRANSACTION) )
748 : {
749 0 : if (! ekr->auth_ok)
750 : {
751 0 : if (ekr->kyc_auth_conflict)
752 0 : return "kyc-wire-impossible";
753 0 : return "kyc-wire-required";
754 : }
755 0 : return "kyc-required";
756 : }
757 : }
758 : }
759 7 : return "ready";
760 : }
761 1 : if (! ekr->auth_ok)
762 : {
763 1 : if (ekr->kyc_auth_conflict)
764 0 : return "kyc-wire-impossible";
765 1 : return "kyc-wire-required";
766 : }
767 0 : if (ekr->in_aml_review)
768 0 : return "awaiting-aml-review";
769 0 : switch (ekr->last_http_status)
770 : {
771 0 : case 0:
772 0 : return "exchange-unreachable";
773 0 : case MHD_HTTP_OK:
774 : /* then we should have kyc_ok */
775 0 : GNUNET_break (0);
776 0 : return NULL;
777 0 : case MHD_HTTP_ACCEPTED:
778 : /* Then KYC is really what is needed */
779 0 : return "kyc-required";
780 0 : case MHD_HTTP_NO_CONTENT:
781 : /* then we should have had kyc_ok! */
782 0 : GNUNET_break (0);
783 0 : return NULL;
784 0 : case MHD_HTTP_FORBIDDEN:
785 : /* then we should have had ! auth_ok */
786 0 : GNUNET_break (0);
787 0 : return NULL;
788 0 : case MHD_HTTP_NOT_FOUND:
789 : /* then we should have had ! auth_ok */
790 0 : GNUNET_break (0);
791 0 : return NULL;
792 0 : case MHD_HTTP_CONFLICT:
793 : /* then we should have had ! auth_ok */
794 0 : GNUNET_break (0);
795 0 : return NULL;
796 0 : case MHD_HTTP_INTERNAL_SERVER_ERROR:
797 0 : return "exchange-internal-error";
798 0 : case MHD_HTTP_GATEWAY_TIMEOUT:
799 0 : return "exchange-gateway-timeout";
800 0 : default:
801 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
802 : "Exchange responded with unexpected HTTP status %u to /kyc-check request!\n",
803 : ekr->last_http_status);
804 0 : break;
805 : }
806 0 : return "exchange-status-invalid";
807 : }
808 :
809 :
810 : /**
811 : * We have found an exchange in status @a status. Clear any
812 : * long-pollers that wait for us having (or not having) this
813 : * status.
814 : *
815 : * @param[in,out] kc context
816 : * @param status the status we encountered
817 : */
818 : static void
819 12 : clear_status (struct KycContext *kc,
820 : const char *status)
821 : {
822 12 : if ( (NULL != kc->lp_status) &&
823 0 : (0 == strcmp (kc->lp_status,
824 : status)) )
825 0 : kc->lp_status = NULL; /* satisfied! */
826 12 : if ( (NULL != kc->lp_not_status) &&
827 0 : (0 != strcmp (kc->lp_not_status,
828 : status) ) )
829 0 : kc->lp_not_status = NULL; /* satisfied! */
830 12 : }
831 :
832 :
833 : /**
834 : * Pack the given @a limit into the JSON @a limits array.
835 : *
836 : * @param kc overall request context
837 : * @param limit account limit to pack
838 : * @param[in,out] limits JSON array to extend
839 : */
840 : static void
841 0 : pack_limit (const struct KycContext *kc,
842 : const struct TALER_EXCHANGE_AccountLimit *limit,
843 : json_t *limits)
844 : {
845 : json_t *jl;
846 :
847 0 : jl = GNUNET_JSON_PACK (
848 : TALER_JSON_pack_kycte ("operation_type",
849 : limit->operation_type),
850 : (POF_TEXT == kc->format)
851 : ? GNUNET_JSON_pack_string ("interval",
852 : GNUNET_TIME_relative2s (limit->timeframe,
853 : true))
854 : : GNUNET_JSON_pack_time_rel ("timeframe",
855 : limit->timeframe),
856 : TALER_JSON_pack_amount ("threshold",
857 : &limit->threshold),
858 : GNUNET_JSON_pack_bool ("soft_limit",
859 : limit->soft_limit)
860 : );
861 0 : GNUNET_assert (0 ==
862 : json_array_append_new (limits,
863 : jl));
864 0 : }
865 :
866 :
867 : /**
868 : * Return JSON array with AccountLimit objects giving
869 : * the current limits for this exchange.
870 : *
871 : * @param[in,out] ekr overall request context
872 : */
873 : static json_t *
874 10 : get_exchange_limits (
875 : struct ExchangeKycRequest *ekr)
876 : {
877 10 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
878 : json_t *limits;
879 :
880 10 : if (NULL != ekr->jlimits)
881 : {
882 4 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
883 : "Returning custom KYC limits\n");
884 4 : return json_incref (ekr->jlimits);
885 : }
886 6 : if (NULL == keys)
887 : {
888 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
889 : "No keys, thus no default KYC limits known\n");
890 0 : return NULL;
891 : }
892 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
893 : "Returning default KYC limits (%u/%u)\n",
894 : keys->hard_limits_length,
895 : keys->zero_limits_length);
896 6 : limits = json_array ();
897 6 : GNUNET_assert (NULL != limits);
898 6 : for (unsigned int i = 0; i<keys->hard_limits_length; i++)
899 : {
900 0 : const struct TALER_EXCHANGE_AccountLimit *limit
901 0 : = &keys->hard_limits[i];
902 :
903 0 : pack_limit (ekr->kc,
904 : limit,
905 : limits);
906 : }
907 7 : for (unsigned int i = 0; i<keys->zero_limits_length; i++)
908 : {
909 1 : const struct TALER_EXCHANGE_ZeroLimitedOperation *zlimit
910 1 : = &keys->zero_limits[i];
911 : json_t *jl;
912 : struct TALER_Amount zero;
913 :
914 1 : GNUNET_assert (GNUNET_OK ==
915 : TALER_amount_set_zero (keys->currency,
916 : &zero));
917 1 : jl = GNUNET_JSON_PACK (
918 : TALER_JSON_pack_kycte ("operation_type",
919 : zlimit->operation_type),
920 : GNUNET_JSON_pack_bool (
921 : "disallowed",
922 : true),
923 : (POF_TEXT == ekr->kc->format)
924 : ? GNUNET_JSON_pack_string (
925 : "interval",
926 : GNUNET_TIME_relative2s (GNUNET_TIME_UNIT_ZERO,
927 : true))
928 : : GNUNET_JSON_pack_time_rel ("timeframe",
929 : GNUNET_TIME_UNIT_ZERO),
930 : TALER_JSON_pack_amount ("threshold",
931 : &zero),
932 : GNUNET_JSON_pack_bool ("soft_limit",
933 : true)
934 : );
935 1 : GNUNET_assert (0 ==
936 : json_array_append_new (limits,
937 : jl));
938 : }
939 6 : return limits;
940 : }
941 :
942 :
943 : /**
944 : * Take data from @a ekr to expand our response.
945 : *
946 : * @param ekr exchange we are done inspecting
947 : */
948 : static void
949 10 : ekr_expand_response (struct ExchangeKycRequest *ekr)
950 : {
951 10 : const struct KycContext *kc = ekr->kc;
952 10 : struct TMH_Exchange *e = TMH_EXCHANGES_lookup_exchange (ekr->exchange_url);
953 : const char *status;
954 : const char *q;
955 : char *short_account;
956 10 : bool kyc_swap_tos_acceptance = false;
957 10 : char *tos_accepted_early = NULL;
958 :
959 10 : GNUNET_assert (NULL != e);
960 10 : status = map_to_status (ekr);
961 10 : if (NULL == status)
962 : {
963 0 : GNUNET_break (0);
964 0 : status = "logic-bug";
965 : }
966 10 : clear_status (ekr->kc,
967 : status);
968 10 : q = strchr (ekr->payto_uri.full_payto,
969 : '?');
970 10 : if (NULL == q)
971 0 : short_account = GNUNET_strdup (ekr->payto_uri.full_payto);
972 : else
973 10 : short_account = GNUNET_strndup (ekr->payto_uri.full_payto,
974 : q - ekr->payto_uri.full_payto);
975 10 : if (NULL != ekr->keys)
976 6 : kyc_swap_tos_acceptance = ekr->keys->kyc_swap_tos_acceptance;
977 : {
978 : enum GNUNET_DB_QueryStatus qs;
979 :
980 10 : qs = TALER_MERCHANTDB_set_instance (
981 : TMH_db,
982 10 : kc->mi->settings.id);
983 10 : if (0 >= qs)
984 : {
985 0 : GNUNET_break (0);
986 0 : tos_accepted_early = NULL;
987 : }
988 : else
989 : {
990 10 : qs = TALER_MERCHANTDB_get_tos_accepted_early (TMH_db,
991 10 : kc->mi->settings.id,
992 10 : ekr->exchange_url,
993 : &tos_accepted_early);
994 10 : GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
995 : TALER_MERCHANTDB_set_instance (
996 : TMH_db,
997 : NULL));
998 10 : if (qs < 0)
999 : {
1000 0 : GNUNET_break (0);
1001 : /* fall through with tos_accepted_early == NULL */
1002 0 : tos_accepted_early = NULL;
1003 : }
1004 : }
1005 : }
1006 10 : GNUNET_assert (
1007 : 0 ==
1008 : json_array_append_new (
1009 : ekr->kc->kycs_data,
1010 : GNUNET_JSON_PACK (
1011 : (POF_TEXT == kc->format)
1012 : ? GNUNET_JSON_pack_string (
1013 : "short_payto_uri",
1014 : short_account)
1015 : : TALER_JSON_pack_full_payto (
1016 : "payto_uri",
1017 : ekr->payto_uri),
1018 : GNUNET_JSON_pack_data_auto (
1019 : "h_wire",
1020 : &ekr->h_wire),
1021 : GNUNET_JSON_pack_string (
1022 : "status",
1023 : status),
1024 : GNUNET_JSON_pack_string (
1025 : "exchange_url",
1026 : ekr->exchange_url),
1027 : GNUNET_JSON_pack_string (
1028 : "exchange_currency",
1029 : TMH_EXCHANGES_get_currency (e)),
1030 : GNUNET_JSON_pack_bool ("no_keys",
1031 : ekr->no_keys),
1032 : GNUNET_JSON_pack_bool ("auth_conflict",
1033 : ekr->kyc_auth_conflict),
1034 : GNUNET_JSON_pack_bool ("kyc_swap_tos_acceptance",
1035 : kyc_swap_tos_acceptance),
1036 : GNUNET_JSON_pack_allow_null (
1037 : GNUNET_JSON_pack_string (
1038 : "tos_accepted_early",
1039 : tos_accepted_early)),
1040 : GNUNET_JSON_pack_uint64 ("exchange_http_status",
1041 : ekr->last_http_status),
1042 : GNUNET_JSON_pack_conditional (
1043 : TALER_EC_NONE != ekr->last_ec,
1044 : GNUNET_JSON_pack_uint64 ("exchange_code",
1045 : ekr->last_ec)),
1046 : GNUNET_JSON_pack_conditional (
1047 : ekr->auth_ok,
1048 : GNUNET_JSON_pack_data_auto (
1049 : "access_token",
1050 : &ekr->access_token)),
1051 : GNUNET_JSON_pack_allow_null (
1052 : GNUNET_JSON_pack_array_steal (
1053 : "limits",
1054 : get_exchange_limits (ekr))),
1055 : GNUNET_JSON_pack_allow_null (
1056 : GNUNET_JSON_pack_array_incref ("payto_kycauths",
1057 : ekr->pkaa))
1058 : )));
1059 10 : GNUNET_free (tos_accepted_early);
1060 10 : GNUNET_free (short_account);
1061 10 : }
1062 :
1063 :
1064 : /**
1065 : * We are done with the KYC request @a ekr. Remove it from the work list and
1066 : * check if we are done overall.
1067 : *
1068 : * @param[in] ekr key request that is done (and will be freed)
1069 : */
1070 : static void
1071 10 : ekr_finished (struct ExchangeKycRequest *ekr)
1072 : {
1073 10 : struct KycContext *kc = ekr->kc;
1074 :
1075 10 : ekr_expand_response (ekr);
1076 10 : ekr_cleanup (ekr);
1077 10 : if (NULL != kc->exchange_pending_head)
1078 2 : return; /* wait for more */
1079 8 : if (kc->in_db)
1080 4 : return;
1081 4 : GNUNET_assert (GNUNET_YES == kc->suspended);
1082 4 : kc->phase = PHASE_GENERATE_RESPONSE;
1083 4 : kc->suspended = GNUNET_NO;
1084 4 : MHD_resume_connection (kc->connection);
1085 4 : TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
1086 : }
1087 :
1088 :
1089 : /**
1090 : * Figure out which exchange accounts from @a keys could
1091 : * be used for a KYC auth wire transfer from the account
1092 : * that @a ekr is checking. Will set the "pkaa" array
1093 : * in @a ekr.
1094 : *
1095 : * @param[in,out] ekr request we are processing
1096 : */
1097 : static void
1098 6 : determine_eligible_accounts (
1099 : struct ExchangeKycRequest *ekr)
1100 : {
1101 6 : struct KycContext *kc = ekr->kc;
1102 6 : const struct TALER_EXCHANGE_Keys *keys = ekr->keys;
1103 : struct TALER_Amount kyc_amount;
1104 : char *merchant_pub_str;
1105 : struct TALER_NormalizedPayto np;
1106 :
1107 : {
1108 : const struct TALER_EXCHANGE_GlobalFee *gf;
1109 :
1110 6 : gf = TALER_EXCHANGE_get_global_fee (keys,
1111 : GNUNET_TIME_timestamp_get ());
1112 6 : if (NULL == gf)
1113 : {
1114 0 : GNUNET_assert (GNUNET_OK ==
1115 : TALER_amount_set_zero (keys->currency,
1116 : &kyc_amount));
1117 : }
1118 : else
1119 : {
1120 : /* FIXME-#9427: history fee should be globally renamed to KYC fee... */
1121 6 : kyc_amount = gf->fees.history;
1122 : }
1123 : }
1124 :
1125 : merchant_pub_str
1126 6 : = GNUNET_STRINGS_data_to_string_alloc (
1127 6 : &kc->mi->merchant_pub,
1128 : sizeof (kc->mi->merchant_pub));
1129 : /* For all accounts of the exchange */
1130 6 : np = TALER_payto_normalize (ekr->payto_uri);
1131 12 : for (unsigned int i = 0; i<keys->accounts_len; i++)
1132 : {
1133 6 : const struct TALER_EXCHANGE_WireAccount *account
1134 6 : = &keys->accounts[i];
1135 :
1136 : /* KYC auth transfers are never supported with conversion */
1137 6 : if (NULL != account->conversion_url)
1138 0 : continue;
1139 : /* filter by source account by credit_restrictions */
1140 6 : if (GNUNET_YES !=
1141 6 : TALER_EXCHANGE_test_account_allowed (account,
1142 : true, /* credit */
1143 : np))
1144 0 : continue;
1145 : /* exchange account is allowed, add it */
1146 : // FIXME: #11520: support short wire transfer subjects!
1147 : // if (NULL != account->prepared_transfer_url) // ...
1148 : {
1149 6 : const char *exchange_account_payto
1150 : = account->fpayto_uri.full_payto;
1151 : char *payto_kycauth;
1152 :
1153 6 : if (TALER_amount_is_zero (&kyc_amount))
1154 0 : GNUNET_asprintf (&payto_kycauth,
1155 : "%s%cmessage=KYC:%s",
1156 : exchange_account_payto,
1157 0 : (NULL == strchr (exchange_account_payto,
1158 : '?'))
1159 : ? '?'
1160 : : '&',
1161 : merchant_pub_str);
1162 : else
1163 12 : GNUNET_asprintf (&payto_kycauth,
1164 : "%s%camount=%s&message=KYC:%s",
1165 : exchange_account_payto,
1166 6 : (NULL == strchr (exchange_account_payto,
1167 : '?'))
1168 : ? '?'
1169 : : '&',
1170 : TALER_amount2s (&kyc_amount),
1171 : merchant_pub_str);
1172 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1173 : "Found account %s where KYC auth is possible\n",
1174 : payto_kycauth);
1175 6 : GNUNET_assert (0 ==
1176 : json_array_append_new (ekr->pkaa,
1177 : json_string (payto_kycauth)));
1178 6 : GNUNET_free (payto_kycauth);
1179 : }
1180 : }
1181 6 : GNUNET_free (np.normalized_payto);
1182 6 : GNUNET_free (merchant_pub_str);
1183 6 : }
1184 :
1185 :
1186 : /**
1187 : * Function called with the result of a #TMH_EXCHANGES_keys4exchange()
1188 : * operation. Runs the KYC check against the exchange.
1189 : *
1190 : * @param cls closure with our `struct ExchangeKycRequest *`
1191 : * @param keys keys of the exchange context
1192 : * @param exchange representation of the exchange
1193 : */
1194 : static void
1195 6 : kyc_with_exchange (void *cls,
1196 : struct TALER_EXCHANGE_Keys *keys,
1197 : struct TMH_Exchange *exchange)
1198 : {
1199 6 : struct ExchangeKycRequest *ekr = cls;
1200 :
1201 : (void) exchange;
1202 6 : ekr->fo = NULL;
1203 6 : if (NULL == keys)
1204 : {
1205 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
1206 : "Failed to download `%skeys`\n",
1207 : ekr->exchange_url);
1208 0 : ekr->no_keys = true;
1209 0 : ekr_finished (ekr);
1210 0 : return;
1211 : }
1212 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1213 : "Got /keys for `%s'\n",
1214 : ekr->exchange_url);
1215 6 : ekr->keys = TALER_EXCHANGE_keys_incref (keys);
1216 6 : if (! ekr->auth_ok)
1217 : {
1218 6 : ekr->pkaa = json_array ();
1219 6 : GNUNET_assert (NULL != ekr->pkaa);
1220 6 : determine_eligible_accounts (ekr);
1221 6 : if (0 == json_array_size (ekr->pkaa))
1222 : {
1223 : /* No KYC auth wire transfers are possible to this exchange from
1224 : our merchant bank account, so we cannot use this account with
1225 : this exchange if it has any KYC requirements! */
1226 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1227 : "KYC auth to `%s' impossible for merchant account `%s'\n",
1228 : ekr->exchange_url,
1229 : ekr->payto_uri.full_payto);
1230 0 : ekr->kyc_auth_conflict = true;
1231 : }
1232 : }
1233 6 : ekr_finished (ekr);
1234 : }
1235 :
1236 :
1237 : /**
1238 : * Closure for add_unreachable_status().
1239 : */
1240 : struct UnreachableContext
1241 : {
1242 : /**
1243 : * Where we are building the response.
1244 : */
1245 : struct KycContext *kc;
1246 :
1247 : /**
1248 : * Pointer to our account hash.
1249 : */
1250 : const struct TALER_MerchantWireHashP *h_wire;
1251 :
1252 : /**
1253 : * Bank account for which we have no status from any exchange.
1254 : */
1255 : struct TALER_FullPayto payto_uri;
1256 :
1257 : };
1258 :
1259 :
1260 : /**
1261 : * Add all trusted exchanges with "unknown" status for the
1262 : * bank account given in the context.
1263 : *
1264 : * @param cls a `struct UnreachableContext`
1265 : * @param url base URL of the exchange
1266 : * @param exchange internal handle for the exchange
1267 : */
1268 : static void
1269 2 : add_unreachable_status (void *cls,
1270 : const char *url,
1271 : const struct TMH_Exchange *exchange)
1272 : {
1273 2 : struct UnreachableContext *uc = cls;
1274 2 : struct KycContext *kc = uc->kc;
1275 :
1276 2 : clear_status (kc,
1277 : "exchange-unreachable");
1278 2 : GNUNET_assert (
1279 : 0 ==
1280 : json_array_append_new (
1281 : kc->kycs_data,
1282 : GNUNET_JSON_PACK (
1283 : TALER_JSON_pack_full_payto (
1284 : "payto_uri",
1285 : uc->payto_uri),
1286 : GNUNET_JSON_pack_data_auto (
1287 : "h_wire",
1288 : uc->h_wire),
1289 : GNUNET_JSON_pack_string (
1290 : "exchange_currency",
1291 : TMH_EXCHANGES_get_currency (exchange)),
1292 : GNUNET_JSON_pack_string (
1293 : "status",
1294 : "exchange-unreachable"),
1295 : GNUNET_JSON_pack_string (
1296 : "exchange_url",
1297 : url),
1298 : GNUNET_JSON_pack_bool ("no_keys",
1299 : true),
1300 : GNUNET_JSON_pack_bool ("auth_conflict",
1301 : false),
1302 : GNUNET_JSON_pack_uint64 ("exchange_http_status",
1303 : 0)
1304 : )));
1305 :
1306 2 : }
1307 :
1308 :
1309 : /**
1310 : * Function called from iterate_kyc_statuses() with KYC status information
1311 : * for this merchant.
1312 : *
1313 : * @param cls our `struct KycContext *`
1314 : * @param h_wire hash of the wire account
1315 : * @param payto_uri payto:// URI of the merchant's bank account
1316 : * @param exchange_url base URL of the exchange for which this is a status
1317 : * @param last_check when did we last get an update on our KYC status from the exchange
1318 : * @param kyc_ok true if we satisfied the KYC requirements
1319 : * @param access_token access token for the KYC SPA, NULL if we cannot access it yet (need KYC auth wire transfer)
1320 : * @param last_http_status last HTTP status from /kyc-check
1321 : * @param last_ec last Taler error code from /kyc-check
1322 : * @param in_aml_review true if the account is pending review
1323 : * @param jlimits JSON array of applicable AccountLimits, or NULL if unknown (like defaults apply)
1324 : */
1325 : static void
1326 12 : kyc_status_cb (
1327 : void *cls,
1328 : const struct TALER_MerchantWireHashP *h_wire,
1329 : struct TALER_FullPayto payto_uri,
1330 : const char *exchange_url,
1331 : struct GNUNET_TIME_Timestamp last_check,
1332 : bool kyc_ok,
1333 : const struct TALER_AccountAccessTokenP *access_token,
1334 : unsigned int last_http_status,
1335 : enum TALER_ErrorCode last_ec,
1336 : bool in_aml_review,
1337 : const json_t *jlimits)
1338 : {
1339 12 : struct KycContext *kc = cls;
1340 : struct ExchangeKycRequest *ekr;
1341 :
1342 12 : if (NULL == exchange_url)
1343 : {
1344 2 : struct UnreachableContext uc = {
1345 : .kc = kc,
1346 : .h_wire = h_wire,
1347 : .payto_uri = payto_uri
1348 : };
1349 :
1350 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1351 : "Account has unknown KYC status for all exchanges.\n");
1352 2 : TMH_exchange_get_trusted (&add_unreachable_status,
1353 : &uc);
1354 2 : return;
1355 : }
1356 10 : if (! TMH_EXCHANGES_check_trusted (exchange_url))
1357 : {
1358 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1359 : "Skipping exchange `%s': not trusted\n",
1360 : exchange_url);
1361 0 : return;
1362 : }
1363 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1364 : "KYC status for `%s' at `%s' is %u/%s/%s/%s\n",
1365 : payto_uri.full_payto,
1366 : exchange_url,
1367 : last_http_status,
1368 : kyc_ok ? "KYC OK" : "KYC NEEDED",
1369 : in_aml_review ? "IN AML REVIEW" : "NO AML REVIEW",
1370 : NULL == jlimits ? "DEFAULT LIMITS" : "CUSTOM LIMITS");
1371 10 : switch (kc->lpt)
1372 : {
1373 6 : case TALER_EXCHANGE_KLPT_NONE:
1374 6 : break;
1375 3 : case TALER_EXCHANGE_KLPT_KYC_AUTH_TRANSFER:
1376 3 : if (NULL != access_token)
1377 3 : kc->return_immediately = true;
1378 3 : break;
1379 0 : case TALER_EXCHANGE_KLPT_INVESTIGATION_DONE:
1380 0 : if (! in_aml_review)
1381 0 : kc->return_immediately = true;
1382 0 : break;
1383 1 : case TALER_EXCHANGE_KLPT_KYC_OK:
1384 1 : if (kyc_ok)
1385 1 : kc->return_immediately = true;
1386 1 : break;
1387 : }
1388 10 : ekr = GNUNET_new (struct ExchangeKycRequest);
1389 10 : GNUNET_CONTAINER_DLL_insert (kc->exchange_pending_head,
1390 : kc->exchange_pending_tail,
1391 : ekr);
1392 10 : ekr->last_http_status = last_http_status;
1393 10 : ekr->last_ec = last_ec;
1394 10 : if (NULL != jlimits)
1395 4 : ekr->jlimits = json_incref ((json_t *) jlimits);
1396 10 : ekr->h_wire = *h_wire;
1397 10 : ekr->exchange_url = GNUNET_strdup (exchange_url);
1398 : ekr->payto_uri.full_payto
1399 10 : = GNUNET_strdup (payto_uri.full_payto);
1400 10 : ekr->last_check = last_check;
1401 10 : ekr->kyc_ok = kyc_ok;
1402 10 : ekr->kc = kc;
1403 10 : ekr->in_aml_review = in_aml_review;
1404 10 : ekr->auth_ok = (NULL != access_token);
1405 10 : if ( (! ekr->auth_ok) ||
1406 4 : (NULL == ekr->jlimits) )
1407 : {
1408 6 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1409 : "Awaiting /keys from `%s'\n",
1410 : exchange_url);
1411 : /* Figure out wire transfer instructions */
1412 6 : ekr->fo = TMH_EXCHANGES_keys4exchange (
1413 : exchange_url,
1414 : false,
1415 : &kyc_with_exchange,
1416 : ekr);
1417 6 : if (NULL == ekr->fo)
1418 : {
1419 0 : GNUNET_break (0);
1420 0 : ekr_finished (ekr);
1421 0 : return;
1422 : }
1423 6 : return;
1424 : }
1425 4 : ekr->access_token = *access_token;
1426 4 : ekr_finished (ekr);
1427 : }
1428 :
1429 :
1430 : /**
1431 : * Check our database for the KYC status. Determines if we then
1432 : * need to wait on exchange data or have no exchange and can
1433 : * immediately proceed to return 204.
1434 : *
1435 : * @param[in,out] kc connection we are handling
1436 : */
1437 : static void
1438 12 : phase_database_kyc_check (struct KycContext *kc)
1439 : {
1440 : enum GNUNET_DB_QueryStatus qs;
1441 :
1442 12 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1443 : "Checking KYC status for %s (%d/%s)\n",
1444 : kc->mi->settings.id,
1445 : kc->have_h_wire,
1446 : kc->exchange_url);
1447 : /* We may run repeatedly due to long-polling; clear data
1448 : from previous runs first */
1449 12 : GNUNET_break (0 ==
1450 : json_array_clear (kc->kycs_data));
1451 12 : kc->in_db = true;
1452 12 : qs = TALER_MERCHANTDB_iterate_kyc_statuses (
1453 : TMH_db,
1454 12 : kc->mi->settings.id,
1455 12 : kc->have_h_wire
1456 : ? &kc->h_wire
1457 : : NULL,
1458 : kc->exchange_url,
1459 12 : ! kc->refresh_requested,
1460 : &kyc_status_cb,
1461 12 : kc);
1462 12 : kc->in_db = false;
1463 12 : if (qs >= 0)
1464 12 : kc->refresh_requested = true;
1465 12 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1466 : "iterate_kyc_statuses returned %d records\n",
1467 : (int) qs);
1468 12 : switch (qs)
1469 : {
1470 0 : case GNUNET_DB_STATUS_HARD_ERROR:
1471 : case GNUNET_DB_STATUS_SOFT_ERROR:
1472 : /* Database error */
1473 0 : GNUNET_break (0);
1474 0 : finish_request (kc,
1475 : TALER_MHD_reply_with_ec (
1476 : kc->connection,
1477 : TALER_EC_GENERIC_DB_FETCH_FAILED,
1478 : "iterate_kyc_statuses"));
1479 0 : return;
1480 2 : case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
1481 2 : kc->phase = PHASE_NO_ACCOUNTS;
1482 2 : return;
1483 8 : case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
1484 : /* Handled below */
1485 8 : break;
1486 : }
1487 10 : if (NULL == kc->exchange_pending_head)
1488 : {
1489 6 : kc->phase = PHASE_GENERATE_RESPONSE;
1490 6 : return;
1491 : }
1492 4 : MHD_suspend_connection (kc->connection);
1493 4 : kc->suspended = GNUNET_YES;
1494 4 : kc->phase = PHASE_SUSPENDED_ON_EXCHANGE;
1495 : }
1496 :
1497 :
1498 : /* ********************* phase_no_accounts *********** */
1499 :
1500 : /**
1501 : * We have no accounts, return a 204 No content,
1502 : * or suspend if long-polling.
1503 : *
1504 : * @param[in,out] kc connection we are handling
1505 : */
1506 : static void
1507 2 : phase_no_accounts (struct KycContext *kc)
1508 : {
1509 : /* We use an Etag of all zeros for the 204 status code */
1510 : static struct GNUNET_ShortHashCode zero_etag;
1511 : struct MHD_Response *response;
1512 :
1513 : /* no matching accounts, could not have suspended */
1514 2 : GNUNET_assert (GNUNET_NO == kc->suspended);
1515 2 : if (kc->have_lp_not_etag &&
1516 0 : (0 == GNUNET_memcmp (&zero_etag,
1517 0 : &kc->lp_not_etag)) &&
1518 0 : (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
1519 : {
1520 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1521 : "No matching accounts, suspending to wait for this to change\n");
1522 0 : MHD_suspend_connection (kc->connection);
1523 0 : kc->suspended = GNUNET_YES;
1524 0 : kc->phase = PHASE_SUSPENDED_ON_ACCOUNT;
1525 0 : return;
1526 : }
1527 2 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1528 : "No matching accounts, returning empty response\n");
1529 2 : response = MHD_create_response_from_buffer_static (0,
1530 : NULL);
1531 2 : TALER_MHD_add_global_headers (response,
1532 : false);
1533 : {
1534 : char *etag;
1535 :
1536 2 : etag = GNUNET_STRINGS_data_to_string_alloc (&zero_etag,
1537 : sizeof (zero_etag));
1538 2 : GNUNET_break (MHD_YES ==
1539 : MHD_add_response_header (response,
1540 : MHD_HTTP_HEADER_ETAG,
1541 : etag));
1542 2 : GNUNET_free (etag);
1543 : }
1544 2 : finish_request (kc,
1545 : MHD_queue_response (kc->connection,
1546 : MHD_HTTP_NO_CONTENT,
1547 : response));
1548 2 : MHD_destroy_response (response);
1549 : }
1550 :
1551 :
1552 : /* ********************* phase_generate_response *********** */
1553 :
1554 : /**
1555 : * Resume the given KYC context and send the final response. Stores the
1556 : * response in the @a kc and signals MHD to resume the connection. Also
1557 : * ensures MHD runs immediately.
1558 : *
1559 : * @param kc KYC context
1560 : */
1561 : static void
1562 10 : resume_kyc_with_response (struct KycContext *kc)
1563 : {
1564 : struct GNUNET_ShortHashCode sh;
1565 : bool not_modified;
1566 : char *can;
1567 : unsigned int response_code;
1568 : struct MHD_Response *response;
1569 :
1570 : /* The database returns KYC records in a stable order, but entries that
1571 : require exchange /keys data are appended when their asynchronous lookup
1572 : finishes. Sort only after all lookups are done so callback timing and
1573 : synthetic exchange-unreachable entries cannot affect the response or its
1574 : ETag. */
1575 10 : TMH_kyc_data_sort (kc->kycs_data);
1576 10 : can = TALER_JSON_canonicalize (kc->kycs_data);
1577 10 : GNUNET_assert (GNUNET_YES ==
1578 : GNUNET_CRYPTO_hkdf_gnunet (&sh,
1579 : sizeof (sh),
1580 : "KYC-SALT",
1581 : strlen ("KYC-SALT"),
1582 : can,
1583 : strlen (can)));
1584 10 : not_modified = kc->have_lp_not_etag &&
1585 0 : (0 == GNUNET_memcmp (&sh,
1586 : &kc->lp_not_etag));
1587 10 : if (not_modified &&
1588 0 : (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
1589 : {
1590 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1591 : "Status unchanged, not returning response yet\n");
1592 0 : wait_for_account (kc);
1593 0 : GNUNET_free (can);
1594 0 : return;
1595 : }
1596 : {
1597 : const char *inm;
1598 :
1599 10 : inm = MHD_lookup_connection_value (kc->connection,
1600 : MHD_HEADER_KIND,
1601 : MHD_HTTP_HEADER_IF_NONE_MATCH);
1602 10 : if ( (NULL == inm) ||
1603 0 : ('"' != inm[0]) ||
1604 0 : ('"' != inm[strlen (inm) - 1]) ||
1605 0 : (0 != strncmp (inm + 1,
1606 : can,
1607 : strlen (can))) )
1608 10 : not_modified = false; /* must return full response */
1609 : }
1610 10 : GNUNET_free (can);
1611 10 : response_code = not_modified
1612 : ? MHD_HTTP_NOT_MODIFIED
1613 10 : : MHD_HTTP_OK;
1614 10 : switch (kc->format)
1615 : {
1616 9 : case POF_JSON:
1617 9 : response = TALER_MHD_MAKE_JSON_PACK (
1618 : GNUNET_JSON_pack_array_incref ("kyc_data",
1619 : kc->kycs_data));
1620 10 : break;
1621 1 : case POF_TEXT:
1622 : {
1623 : enum GNUNET_GenericReturnValue ret;
1624 : json_t *obj;
1625 :
1626 1 : obj = GNUNET_JSON_PACK (
1627 : GNUNET_JSON_pack_array_incref ("kyc_data",
1628 : kc->kycs_data));
1629 1 : ret = TALER_TEMPLATING_build (kc->connection,
1630 : &response_code,
1631 : "kyc_text",
1632 1 : kc->mi->settings.id,
1633 : NULL,
1634 : obj,
1635 : &response);
1636 1 : json_decref (obj);
1637 1 : switch (ret)
1638 : {
1639 0 : case GNUNET_SYSERR:
1640 : /* failed to even produce a response */
1641 0 : GNUNET_break (0);
1642 0 : kc->phase = PHASE_RETURN_NO;
1643 0 : return;
1644 0 : case GNUNET_NO:
1645 0 : finish_request (kc,
1646 : MHD_queue_response (
1647 : kc->connection,
1648 : response_code,
1649 : response));
1650 0 : MHD_destroy_response (response);
1651 0 : return;
1652 1 : case GNUNET_OK:
1653 1 : TALER_MHD_add_global_headers (response,
1654 : false);
1655 1 : GNUNET_break (MHD_YES ==
1656 : MHD_add_response_header (response,
1657 : MHD_HTTP_HEADER_CONTENT_TYPE,
1658 : "text/plain"));
1659 1 : break;
1660 : } /* switch (ret) */
1661 : }
1662 1 : break;
1663 0 : case POF_PDF:
1664 : // not yet implemented
1665 0 : GNUNET_assert (0);
1666 : break;
1667 : }
1668 : {
1669 : char *etag;
1670 : char *qetag;
1671 :
1672 10 : etag = GNUNET_STRINGS_data_to_string_alloc (&sh,
1673 : sizeof (sh));
1674 10 : GNUNET_asprintf (&qetag,
1675 : "\"%s\"",
1676 : etag);
1677 10 : GNUNET_break (MHD_YES ==
1678 : MHD_add_response_header (response,
1679 : MHD_HTTP_HEADER_ETAG,
1680 : qetag));
1681 10 : GNUNET_free (qetag);
1682 10 : GNUNET_free (etag);
1683 : }
1684 10 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1685 : "Resuming /kyc handling as exchange interaction is done (%u)\n",
1686 : MHD_HTTP_OK);
1687 10 : finish_request (kc,
1688 : MHD_queue_response (
1689 : kc->connection,
1690 : response_code,
1691 : response));
1692 10 : MHD_destroy_response (response);
1693 : }
1694 :
1695 :
1696 : /**
1697 : * We are done with asynchronous processing, generate the
1698 : * response for the @e kc.
1699 : *
1700 : * @param[in,out] kc KYC context to respond for
1701 : */
1702 : static void
1703 10 : phase_generate_response (struct KycContext *kc)
1704 : {
1705 10 : GNUNET_assert (NULL == kc->exchange_pending_head);
1706 10 : GNUNET_assert (GNUNET_NO == kc->suspended);
1707 : /* FIXME: mixing these two suspend conditions like this
1708 : does not seem sane */
1709 10 : if ( (! kc->return_immediately) &&
1710 0 : (! GNUNET_TIME_absolute_is_past (kc->timeout)) )
1711 : {
1712 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1713 : "Suspending: long poll target %d not reached\n",
1714 : kc->lpt);
1715 0 : wait_for_account (kc);
1716 0 : return;
1717 : }
1718 10 : if ( (! GNUNET_TIME_absolute_is_past (kc->timeout)) &&
1719 4 : ( (NULL != kc->lp_not_status) ||
1720 4 : (NULL != kc->lp_status) ) )
1721 : {
1722 0 : GNUNET_log (GNUNET_ERROR_TYPE_INFO,
1723 : "Long-poll target status not reached, not returning response yet\n");
1724 0 : wait_for_account (kc);
1725 0 : return;
1726 : }
1727 : /* All exchange requests done, create final
1728 : big response from cumulated replies */
1729 10 : resume_kyc_with_response (kc);
1730 : }
1731 :
1732 :
1733 : /* ******************* main logic ***************** */
1734 :
1735 : /**
1736 : * Check the KYC status of an instance.
1737 : *
1738 : * @param mi instance to check KYC status of
1739 : * @param connection the MHD connection to handle
1740 : * @param[in,out] hc context with further information about the request
1741 : * @return MHD result code
1742 : */
1743 : static enum MHD_Result
1744 16 : get_instances_ID_kyc (
1745 : struct TMH_MerchantInstance *mi,
1746 : struct MHD_Connection *connection,
1747 : struct TMH_HandlerContext *hc)
1748 : {
1749 16 : struct KycContext *kc = hc->ctx;
1750 :
1751 16 : if (NULL == kc)
1752 : {
1753 12 : kc = GNUNET_new (struct KycContext);
1754 12 : kc->mi = mi;
1755 12 : hc->ctx = kc;
1756 12 : hc->cc = &kyc_context_cleanup;
1757 12 : GNUNET_CONTAINER_DLL_insert (kc_head,
1758 : kc_tail,
1759 : kc);
1760 12 : kc->connection = connection;
1761 12 : kc->hc = hc;
1762 12 : TALER_MHD_parse_request_timeout (connection,
1763 : &kc->timeout);
1764 : {
1765 12 : uint64_t num = 0;
1766 : int val;
1767 :
1768 12 : TALER_MHD_parse_request_number (connection,
1769 : "lpt",
1770 : &num);
1771 12 : val = (int) num;
1772 12 : if ( (val < 0) ||
1773 : (val > TALER_EXCHANGE_KLPT_MAX) )
1774 : {
1775 : /* Protocol violation, but we can be graceful and
1776 : just ignore the long polling! */
1777 0 : GNUNET_break_op (0);
1778 0 : val = TALER_EXCHANGE_KLPT_NONE;
1779 : }
1780 12 : kc->lpt = (enum TALER_EXCHANGE_KycLongPollTarget) val;
1781 : }
1782 : kc->return_immediately
1783 12 : = (TALER_EXCHANGE_KLPT_NONE == kc->lpt);
1784 12 : kc->lp_status = MHD_lookup_connection_value (
1785 : connection,
1786 : MHD_GET_ARGUMENT_KIND,
1787 : "lp_status");
1788 12 : kc->lp_not_status = MHD_lookup_connection_value (
1789 : connection,
1790 : MHD_GET_ARGUMENT_KIND,
1791 : "lp_not_status");
1792 12 : TALER_MHD_parse_request_arg_auto (connection,
1793 : "h_wire",
1794 : &kc->h_wire,
1795 : kc->have_h_wire);
1796 12 : TALER_MHD_parse_request_arg_auto (connection,
1797 : "lp_not_etag",
1798 : &kc->lp_not_etag,
1799 : kc->have_lp_not_etag);
1800 : }
1801 : while (1)
1802 : {
1803 64 : switch (kc->phase)
1804 : {
1805 12 : case PHASE_INIT:
1806 12 : phase_init (kc);
1807 12 : break;
1808 12 : case PHASE_DETERMINE_LONG_POLL:
1809 12 : phase_determine_long_poll (kc);
1810 12 : break;
1811 12 : case PHASE_DATABASE_KYC_CHECK:
1812 12 : phase_database_kyc_check (kc);
1813 12 : break;
1814 2 : case PHASE_NO_ACCOUNTS:
1815 2 : phase_no_accounts (kc);
1816 2 : break;
1817 10 : case PHASE_GENERATE_RESPONSE:
1818 10 : phase_generate_response (kc);
1819 10 : break;
1820 0 : case PHASE_IN_SHUTDOWN:
1821 : /* during shutdown, we don't generate any more replies */
1822 0 : GNUNET_assert (GNUNET_SYSERR == kc->suspended);
1823 0 : return MHD_NO;
1824 12 : case PHASE_RETURN_YES:
1825 12 : return MHD_YES;
1826 0 : case PHASE_RETURN_NO:
1827 0 : return MHD_NO;
1828 0 : case PHASE_SUSPENDED_ON_ACCOUNT:
1829 : /* suspended */
1830 0 : GNUNET_assert (GNUNET_YES == kc->suspended);
1831 0 : return MHD_YES;
1832 4 : case PHASE_SUSPENDED_ON_EXCHANGE:
1833 : /* suspended */
1834 4 : GNUNET_assert (GNUNET_YES == kc->suspended);
1835 4 : return MHD_YES;
1836 : }
1837 : }
1838 : }
1839 :
1840 :
1841 : enum MHD_Result
1842 16 : TMH_private_get_instances_ID_kyc (
1843 : const struct TMH_RequestHandler *rh,
1844 : struct MHD_Connection *connection,
1845 : struct TMH_HandlerContext *hc)
1846 : {
1847 16 : struct TMH_MerchantInstance *mi = hc->instance;
1848 :
1849 : (void) rh;
1850 16 : return get_instances_ID_kyc (mi,
1851 : connection,
1852 : hc);
1853 : }
1854 :
1855 :
1856 : enum MHD_Result
1857 0 : TMH_private_get_instances_default_ID_kyc (
1858 : const struct TMH_RequestHandler *rh,
1859 : struct MHD_Connection *connection,
1860 : struct TMH_HandlerContext *hc)
1861 : {
1862 : struct TMH_MerchantInstance *mi;
1863 :
1864 : (void) rh;
1865 0 : mi = TMH_lookup_instance (hc->infix);
1866 0 : if (NULL == mi)
1867 : {
1868 0 : return TALER_MHD_reply_with_error (
1869 : connection,
1870 : MHD_HTTP_NOT_FOUND,
1871 : TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
1872 0 : hc->infix);
1873 : }
1874 0 : return get_instances_ID_kyc (mi,
1875 : connection,
1876 : hc);
1877 : }
1878 :
1879 :
1880 : /* end of taler-merchant-httpd_get-private-kyc.c */
|