merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

taler-merchant-httpd_get-private-kyc.c (48159B)


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