LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_get-private-kyc.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 66.4 % 533 354
Test Date: 2026-09-04 23:42:01 Functions: 84.0 % 25 21

            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 */
        

Generated by: LCOV version 2.0-1