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