exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

taler-exchange-httpd_kyc-info.c (23315B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2021-2024 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU Affero General Public License for more details.
     12 
     13   You should have received a copy of the GNU Affero General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file taler-exchange-httpd_kyc-info.c
     18  * @brief Handle request for generic KYC info.
     19  * @author Christian Grothoff
     20  */
     21 #include "taler/platform.h"
     22 #include <gnunet/gnunet_util_lib.h>
     23 #include <gnunet/gnunet_json_lib.h>
     24 #include <jansson.h>
     25 #include <microhttpd.h>
     26 #include <pthread.h>
     27 #include "taler/taler_json_lib.h"
     28 #include "taler/taler_exchangedb_lib.h"
     29 #include "taler/taler_kyclogic_lib.h"
     30 #include "taler/taler_mhd_lib.h"
     31 #include "taler/taler_signatures.h"
     32 #include "taler/taler_dbevents.h"
     33 #include "taler-exchange-httpd_keys.h"
     34 #include "taler-exchange-httpd_kyc-info.h"
     35 #include "taler-exchange-httpd_responses.h"
     36 #include "taler-exchange-httpd_common_kyc.h"
     37 
     38 
     39 /**
     40  * Context for the GET /kyc-info request.
     41  *
     42  * Used for long-polling and other asynchronous waiting.
     43  */
     44 struct KycPoller
     45 {
     46   /**
     47    * Kept in a DLL.
     48    */
     49   struct KycPoller *next;
     50 
     51   /**
     52    * Kept in a DLL.
     53    */
     54   struct KycPoller *prev;
     55 
     56   /**
     57    * Connection we are handling.
     58    */
     59   struct MHD_Connection *connection;
     60 
     61   /**
     62    * Subscription for the database event we are
     63    * waiting for.
     64    */
     65   struct GNUNET_DB_EventHandler *eh;
     66 
     67   /**
     68    * Handle to async activity to get the latest legitimization
     69    * rule set.
     70    */
     71   struct TALER_EXCHANGEDB_RuleUpdater *ru;
     72 
     73   /**
     74    * Current legitimization rule set, owned by callee.  Will be NULL on error
     75    * or for default rules. Will not contain skip rules and not be expired.
     76    */
     77   struct TALER_KYCLOGIC_LegitimizationRuleSet *lrs;
     78 
     79   /**
     80    * Handle for async KYC processing.
     81    */
     82   struct TEH_KycMeasureRunContext *kat;
     83 
     84   /**
     85    * Response to return, NULL if none yet.
     86    */
     87   struct MHD_Response *response;
     88 
     89   /**
     90    * Set to access token for a KYC process by the account,
     91    * if @e have_token is true.
     92    */
     93   struct TALER_AccountAccessTokenP access_token;
     94 
     95   /**
     96    * Payto hash of the account matching @a access_token.
     97    */
     98   struct TALER_NormalizedPaytoHashP h_payto;
     99 
    100   /**
    101    * #MHD_HTTP_HEADER_IF_NONE_MATCH Etag value sent by the client.  0 for none
    102    * (or malformed).
    103    */
    104   uint64_t etag_outcome_in;
    105 
    106   /**
    107    * #MHD_HTTP_HEADER_IF_NONE_MATCH Etag value sent by the client.  0 for none
    108    * (or malformed).
    109    */
    110   uint64_t etag_measure_in;
    111 
    112   /**
    113    * When will this request time out?
    114    */
    115   struct GNUNET_TIME_Absolute timeout;
    116 
    117   /**
    118    *
    119    */
    120   uint64_t legitimization_measure_last_row;
    121 
    122   /**
    123    * Row in the legitimization outcomes table that @e lrs matches.
    124    */
    125   uint64_t legitimization_outcome_last_row;
    126 
    127   /**
    128    * HTTP status code to use with @e response.
    129    */
    130   unsigned int response_code;
    131 
    132   /**
    133    * #GNUNET_YES if our @e h_payto is for a wallet,
    134    * #GNUNET_NO if it is for an account,
    135    * #GNUNET_SYSERR if we do not know.
    136    */
    137   enum GNUNET_GenericReturnValue is_wallet;
    138 
    139   /**
    140    * True if we are still suspended.
    141    */
    142   bool suspended;
    143 
    144 };
    145 
    146 
    147 /**
    148  * Head of list of requests in long polling.
    149  */
    150 static struct KycPoller *kyp_head;
    151 
    152 /**
    153  * Tail of list of requests in long polling.
    154  */
    155 static struct KycPoller *kyp_tail;
    156 
    157 
    158 void
    159 TEH_kyc_info_cleanup ()
    160 {
    161   struct KycPoller *kyp;
    162 
    163   while (NULL != (kyp = kyp_head))
    164   {
    165     GNUNET_CONTAINER_DLL_remove (kyp_head,
    166                                  kyp_tail,
    167                                  kyp);
    168     if (kyp->suspended)
    169     {
    170       kyp->suspended = false;
    171       MHD_resume_connection (kyp->connection);
    172     }
    173   }
    174 }
    175 
    176 
    177 /**
    178  * Function called once a connection is done to
    179  * clean up the `struct ReservePoller` state.
    180  *
    181  * @param rc context to clean up for
    182  */
    183 static void
    184 kyp_cleanup (struct TEH_RequestContext *rc)
    185 {
    186   struct KycPoller *kyp = rc->rh_ctx;
    187 
    188   GNUNET_assert (! kyp->suspended);
    189   if (NULL != kyp->eh)
    190   {
    191     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    192                 "Cancelling DB event listening\n");
    193     TEH_plugin->event_listen_cancel (TEH_plugin->cls,
    194                                      kyp->eh);
    195     kyp->eh = NULL;
    196   }
    197   if (NULL != kyp->ru)
    198   {
    199     TALER_EXCHANGEDB_update_rules_cancel (kyp->ru);
    200     kyp->ru = NULL;
    201   }
    202   if (NULL != kyp->response)
    203   {
    204     MHD_destroy_response (kyp->response);
    205     kyp->response = NULL;
    206   }
    207   if (NULL != kyp->lrs)
    208   {
    209     TALER_KYCLOGIC_rules_free (kyp->lrs);
    210     kyp->lrs = NULL;
    211   }
    212   if (NULL != kyp->kat)
    213   {
    214     TEH_kyc_run_measure_cancel (kyp->kat);
    215     kyp->kat = NULL;
    216   }
    217   GNUNET_free (kyp);
    218 }
    219 
    220 
    221 /**
    222  * Function called on events received from Postgres.
    223  * Wakes up long pollers.
    224  *
    225  * @param cls the `struct TEH_RequestContext *`
    226  * @param extra additional event data provided
    227  * @param extra_size number of bytes in @a extra
    228  */
    229 static void
    230 db_event_cb (void *cls,
    231              const void *extra,
    232              size_t extra_size)
    233 {
    234   struct TEH_RequestContext *rc = cls;
    235   struct KycPoller *kyp = rc->rh_ctx;
    236   struct GNUNET_AsyncScopeSave old_scope;
    237 
    238   (void) extra;
    239   (void) extra_size;
    240   if (! kyp->suspended)
    241     return; /* event triggered while main transaction
    242                was still running, or got multiple wake-up events */
    243   GNUNET_async_scope_enter (&rc->async_scope_id,
    244                             &old_scope);
    245   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    246               "Resuming from long-polling on KYC status\n");
    247   GNUNET_CONTAINER_DLL_remove (kyp_head,
    248                                kyp_tail,
    249                                kyp);
    250   kyp->suspended = false;
    251   MHD_resume_connection (kyp->connection);
    252   TALER_MHD_daemon_trigger ();
    253   GNUNET_async_scope_restore (&old_scope);
    254 }
    255 
    256 
    257 /**
    258  * Add the headers we want to set for every response.
    259  *
    260  * @param[in,out] response the response to modify
    261  */
    262 static void
    263 add_nocache_header (struct MHD_Response *response)
    264 {
    265   GNUNET_break (MHD_YES ==
    266                 MHD_add_response_header (response,
    267                                          MHD_HTTP_HEADER_CACHE_CONTROL,
    268                                          "no-cache"));
    269 }
    270 
    271 
    272 /**
    273  * Resume processing the @a kyp request with the @a response.
    274  *
    275  * @param kyp request to resume and respond to
    276  * @param http_status HTTP status for @a response
    277  * @param response HTTP response to return
    278  */
    279 static void
    280 resume_with_response (struct KycPoller *kyp,
    281                       unsigned int http_status,
    282                       struct MHD_Response *response)
    283 {
    284   kyp->response_code = http_status;
    285   kyp->response = response;
    286   GNUNET_CONTAINER_DLL_remove (kyp_head,
    287                                kyp_tail,
    288                                kyp);
    289   kyp->suspended = false;
    290   MHD_resume_connection (kyp->connection);
    291   TALER_MHD_daemon_trigger ();
    292 }
    293 
    294 
    295 /**
    296  * Function called after a measure has been run.
    297  *
    298  * @param kyp request to fail with the error code
    299  * @param ec error code or 0 on success
    300  * @param hint detail error message or NULL on success / no info
    301  */
    302 static void
    303 fail_with_ec (
    304   struct KycPoller *kyp,
    305   enum TALER_ErrorCode ec,
    306   const char *hint)
    307 {
    308   resume_with_response (kyp,
    309                         TALER_ErrorCode_get_http_status (ec),
    310                         TALER_MHD_make_error (ec,
    311                                               hint));
    312 }
    313 
    314 
    315 /**
    316  * Generate a reply with the KycProcessClientInformation from
    317  * the LegitimizationMeasures.
    318  *
    319  * @param[in,out] kyp request to reply on
    320  * @param legitimization_measure_row_id part of etag to set for the response
    321  * @param legitimization_outcome_row_id part of etag to set for the response
    322  * @param jmeasures a `LegitimizationMeasures` object to encode
    323  * @param jvoluntary array of voluntary measures to encode, can be NULL
    324  */
    325 static void
    326 resume_with_reply (struct KycPoller *kyp,
    327                    uint64_t legitimization_measure_row_id,
    328                    uint64_t legitimization_outcome_row_id,
    329                    const json_t *jmeasures,
    330                    const json_t *jvoluntary)
    331 {
    332   const json_t *measures; /* array of MeasureInformation */
    333   bool is_and_combinator = false;
    334   bool verboten;
    335   struct GNUNET_JSON_Specification spec[] = {
    336     GNUNET_JSON_spec_mark_optional (
    337       GNUNET_JSON_spec_bool ("is_and_combinator",
    338                              &is_and_combinator),
    339       NULL),
    340     GNUNET_JSON_spec_bool ("verboten",
    341                            &verboten),
    342     GNUNET_JSON_spec_array_const ("measures",
    343                                   &measures),
    344     GNUNET_JSON_spec_end ()
    345   };
    346   enum GNUNET_GenericReturnValue ret;
    347   const char *ename;
    348   unsigned int eline;
    349   json_t *kris;
    350   size_t i;
    351   json_t *mi; /* a MeasureInformation object */
    352 
    353   ret = GNUNET_JSON_parse (jmeasures,
    354                            spec,
    355                            &ename,
    356                            &eline);
    357   if (GNUNET_OK != ret)
    358   {
    359     GNUNET_break (0);
    360     fail_with_ec (kyp,
    361                   TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    362                   ename);
    363     return;
    364   }
    365   kris = json_array ();
    366   GNUNET_assert (NULL != kris);
    367   json_array_foreach ((json_t *) measures, i, mi)
    368   {
    369     const char *check_name;
    370     const json_t *context = NULL;
    371     struct GNUNET_JSON_Specification ispec[] = {
    372       GNUNET_JSON_spec_string ("check_name",
    373                                &check_name),
    374       GNUNET_JSON_spec_mark_optional (
    375         GNUNET_JSON_spec_object_const ("context",
    376                                        &context),
    377         NULL),
    378       GNUNET_JSON_spec_end ()
    379     };
    380     json_t *kri;
    381 
    382     ret = GNUNET_JSON_parse (mi,
    383                              ispec,
    384                              &ename,
    385                              &eline);
    386     if (GNUNET_OK != ret)
    387     {
    388       GNUNET_break (0);
    389       json_decref (kris);
    390       fail_with_ec (
    391         kyp,
    392         TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    393         ename);
    394       return;
    395     }
    396     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    397                 "Found required check `%s'\n",
    398                 check_name);
    399     if (NULL != context)
    400     {
    401       json_dumpf (context,
    402                   stderr,
    403                   JSON_INDENT (2));
    404       fprintf (stderr,
    405                "\n");
    406     }
    407     /* Check if requirement is a duplicate, and in that case do
    408        not return it */
    409     {
    410       bool duplicate = false;
    411 
    412       for (size_t off = 0; off < i; off++)
    413       {
    414         json_t *have = json_array_get (measures,
    415                                        off);
    416         if ( (1 ==
    417               json_equal (json_object_get (have,
    418                                            "check_name"),
    419                           json_object_get (mi,
    420                                            "check_name")) ) &&
    421              (1 ==
    422               json_equal (json_object_get (have,
    423                                            "check_name"),
    424                           json_object_get (mi,
    425                                            "check_name")) ) &&
    426              (1 ==
    427               json_equal (json_object_get (have,
    428                                            "check_name"),
    429                           json_object_get (mi,
    430                                            "check_name")) ) )
    431         {
    432           /* Duplicate requirement, do not return again */
    433           duplicate = true;
    434           break;
    435         }
    436       }
    437       if (duplicate)
    438       {
    439         continue;
    440       }
    441     }
    442     kri = TALER_KYCLOGIC_measure_to_requirement (
    443       check_name,
    444       context,
    445       &kyp->access_token,
    446       i,
    447       legitimization_measure_row_id);
    448     if (NULL == kri)
    449     {
    450       GNUNET_break (0);
    451       json_decref (kris);
    452       fail_with_ec (
    453         kyp,
    454         TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
    455         "could not convert measure to requirement");
    456       return;
    457     }
    458     GNUNET_assert (0 ==
    459                    json_array_append_new (kris,
    460                                           kri));
    461   }
    462 
    463   {
    464     char etags[128];
    465     struct MHD_Response *resp;
    466 
    467     GNUNET_snprintf (etags,
    468                      sizeof (etags),
    469                      "\"%llu-%llu\"",
    470                      (unsigned long long) legitimization_measure_row_id,
    471                      (unsigned long long) legitimization_outcome_row_id);
    472     resp = TALER_MHD_MAKE_JSON_PACK (
    473       GNUNET_JSON_pack_array_steal ("requirements",
    474                                     kris),
    475       GNUNET_JSON_pack_bool ("is_and_combinator",
    476                              is_and_combinator),
    477       GNUNET_JSON_pack_allow_null (
    478         GNUNET_JSON_pack_array_incref (
    479           "voluntary_measures",
    480           (json_t *) jvoluntary)));
    481     GNUNET_break (MHD_YES ==
    482                   MHD_add_response_header (resp,
    483                                            MHD_HTTP_HEADER_ETAG,
    484                                            etags));
    485     add_nocache_header (resp);
    486     resume_with_response (kyp,
    487                           MHD_HTTP_OK,
    488                           resp);
    489   }
    490 }
    491 
    492 
    493 /**
    494  * Function called with the current rule set.
    495  *
    496  * @param cls closure with a `struct KycPoller *`
    497  * @param rur includes legitimziation rule set that applies to the account
    498  *   (owned by callee, callee must free the lrs!)
    499  */
    500 static void
    501 current_rules_cb (
    502   void *cls,
    503   struct TALER_EXCHANGEDB_RuleUpdaterResult *rur)
    504 {
    505   struct KycPoller *kyp = cls;
    506   enum GNUNET_DB_QueryStatus qs;
    507   uint64_t legitimization_measure_last_row;
    508   json_t *jmeasures;
    509   json_t *vmeasures;
    510 
    511   kyp->ru = NULL;
    512   if (TALER_EC_NONE != rur->ec)
    513   {
    514     /* Rollback should not be needed, just to be sure */
    515     TEH_plugin->rollback (TEH_plugin->cls);
    516     fail_with_ec (kyp,
    517                   rur->ec,
    518                   rur->hint);
    519     return;
    520   }
    521   GNUNET_assert (NULL == kyp->lrs);
    522   kyp->lrs
    523     = rur->lrs;
    524   kyp->legitimization_outcome_last_row
    525     = rur->legitimization_outcome_last_row;
    526   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    527               "LRS is for the account uses %s\n",
    528               NULL == kyp->lrs
    529               ? "default rules"
    530               : "custom rules");
    531 
    532   /* Check if there is an unfinished legitimization measure */
    533   qs = TEH_plugin->lookup_kyc_status_by_token (
    534     TEH_plugin->cls,
    535     &kyp->access_token,
    536     &legitimization_measure_last_row,
    537     &jmeasures);
    538   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    539               "lookup_kyc_status_by_token returned %d\n",
    540               (int) qs);
    541   if (qs < 0)
    542   {
    543     GNUNET_break (0);
    544     TEH_plugin->rollback (TEH_plugin->cls);
    545     fail_with_ec (
    546       kyp,
    547       TALER_EC_GENERIC_DB_FETCH_FAILED,
    548       "lookup_kyc_status_by_token");
    549     return;
    550   }
    551   if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    552   {
    553     jmeasures
    554       = TALER_KYCLOGIC_zero_measures (kyp->lrs,
    555                                       kyp->is_wallet);
    556     if (NULL == jmeasures)
    557     {
    558       qs = TEH_plugin->commit (TEH_plugin->cls);
    559       if (qs < 0)
    560       {
    561         TEH_plugin->rollback (TEH_plugin->cls);
    562         fail_with_ec (
    563           kyp,
    564           TALER_EC_GENERIC_DB_COMMIT_FAILED,
    565           "kyc-info");
    566         return;
    567       }
    568       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    569                   "No KYC requirement open\n");
    570       resume_with_response (kyp,
    571                             MHD_HTTP_OK,
    572                             TALER_MHD_MAKE_JSON_PACK (
    573                               GNUNET_JSON_pack_allow_null (
    574                                 GNUNET_JSON_pack_array_steal (
    575                                   "voluntary_measures",
    576                                   TALER_KYCLOGIC_voluntary_measures (
    577                                     kyp->lrs))
    578                                 )));
    579       return;
    580     }
    581     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    582                 "Making applicable zero-measures for %s under current rules active\n",
    583                 (GNUNET_SYSERR == kyp->is_wallet)
    584                 ? "unknown account type"
    585                 : ( (GNUNET_YES == kyp->is_wallet)
    586                     ? "wallets"
    587                     : "accounts"));
    588     json_dumpf (jmeasures,
    589                 stderr,
    590                 JSON_INDENT (2));
    591     qs = TEH_plugin->insert_active_legitimization_measure (
    592       TEH_plugin->cls,
    593       &kyp->access_token,
    594       jmeasures,
    595       &legitimization_measure_last_row);
    596     if (qs < 0)
    597     {
    598       GNUNET_break (0);
    599       TEH_plugin->rollback (TEH_plugin->cls);
    600       fail_with_ec (kyp,
    601                     TALER_EC_GENERIC_DB_STORE_FAILED,
    602                     "insert_active_legitimization_measure");
    603       return;
    604     }
    605   }
    606   if ( (legitimization_measure_last_row == kyp->etag_measure_in) &&
    607        (kyp->legitimization_outcome_last_row == kyp->etag_outcome_in) &&
    608        GNUNET_TIME_absolute_is_future (kyp->timeout) )
    609   {
    610     /* Note: in practice this commit should do nothing, but we cannot
    611        trust that the client provided correct etags, and so we must
    612        commit anyway just in case the client lied about the etags. */
    613     qs = TEH_plugin->commit (TEH_plugin->cls);
    614     if (qs < 0)
    615     {
    616       TEH_plugin->rollback (TEH_plugin->cls);
    617       fail_with_ec (
    618         kyp,
    619         TALER_EC_GENERIC_DB_COMMIT_FAILED,
    620         "kyc-info");
    621       return;
    622     }
    623     if (NULL != kyp->lrs)
    624     {
    625       TALER_KYCLOGIC_rules_free (kyp->lrs);
    626       kyp->lrs = NULL;
    627     }
    628     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    629                 "Suspending HTTP request on timeout (%s)\n",
    630                 GNUNET_TIME_relative2s (
    631                   GNUNET_TIME_absolute_get_remaining (
    632                     kyp->timeout),
    633                   true));
    634     GNUNET_assert (NULL != kyp->eh);
    635     GNUNET_break (kyp->suspended);
    636     return;
    637   }
    638   qs = TEH_plugin->commit (TEH_plugin->cls);
    639   if (qs < 0)
    640   {
    641     TEH_plugin->rollback (TEH_plugin->cls);
    642     fail_with_ec (
    643       kyp,
    644       TALER_EC_GENERIC_DB_COMMIT_FAILED,
    645       "kyc-info");
    646     return;
    647   }
    648   if ( (legitimization_measure_last_row ==
    649         kyp->etag_measure_in) &&
    650        (kyp->legitimization_outcome_last_row ==
    651         kyp->etag_outcome_in) )
    652   {
    653     char etags[128];
    654     struct MHD_Response *resp;
    655 
    656     GNUNET_snprintf (etags,
    657                      sizeof (etags),
    658                      "\"%llu-%llu\"",
    659                      (unsigned long long) legitimization_measure_last_row,
    660                      (unsigned long long) kyp->legitimization_outcome_last_row);
    661     resp = MHD_create_response_from_buffer (0,
    662                                             NULL,
    663                                             MHD_RESPMEM_PERSISTENT);
    664     add_nocache_header (resp);
    665     TALER_MHD_add_global_headers (resp,
    666                                   false);
    667     GNUNET_break (MHD_YES ==
    668                   MHD_add_response_header (resp,
    669                                            MHD_HTTP_HEADER_ETAG,
    670                                            etags));
    671     resume_with_response (kyp,
    672                           MHD_HTTP_NOT_MODIFIED,
    673                           resp);
    674     json_decref (jmeasures);
    675     return;
    676   }
    677   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    678               "Generating success reply to kyc-info query\n");
    679   vmeasures = TALER_KYCLOGIC_voluntary_measures (kyp->lrs);
    680   resume_with_reply (kyp,
    681                      legitimization_measure_last_row,
    682                      kyp->legitimization_outcome_last_row,
    683                      jmeasures,
    684                      vmeasures);
    685   json_decref (vmeasures);
    686   json_decref (jmeasures);
    687 }
    688 
    689 
    690 MHD_RESULT
    691 TEH_handler_kyc_info (
    692   struct TEH_RequestContext *rc,
    693   const char *const args[1])
    694 {
    695   struct KycPoller *kyp = rc->rh_ctx;
    696   enum GNUNET_DB_QueryStatus qs;
    697 
    698   if (NULL == kyp)
    699   {
    700     bool bis_wallet;
    701 
    702     kyp = GNUNET_new (struct KycPoller);
    703     kyp->connection = rc->connection;
    704     rc->rh_ctx = kyp;
    705     rc->rh_cleaner = &kyp_cleanup;
    706 
    707     if (GNUNET_OK !=
    708         GNUNET_STRINGS_string_to_data (
    709           args[0],
    710           strlen (args[0]),
    711           &kyp->access_token,
    712           sizeof (kyp->access_token)))
    713     {
    714       GNUNET_break_op (0);
    715       return TALER_MHD_reply_with_error (
    716         rc->connection,
    717         MHD_HTTP_BAD_REQUEST,
    718         TALER_EC_GENERIC_PARAMETER_MALFORMED,
    719         "access token");
    720     }
    721     TALER_MHD_parse_request_timeout (rc->connection,
    722                                      &kyp->timeout);
    723 
    724     /* Get etag */
    725     {
    726       const char *etags;
    727 
    728       etags = MHD_lookup_connection_value (
    729         rc->connection,
    730         MHD_HEADER_KIND,
    731         MHD_HTTP_HEADER_IF_NONE_MATCH);
    732       if (NULL != etags)
    733       {
    734         char dummy;
    735         unsigned long long ev1;
    736         unsigned long long ev2;
    737 
    738         if (2 != sscanf (etags,
    739                          "\"%llu-%llu\"%c",
    740                          &ev1,
    741                          &ev2,
    742                          &dummy))
    743         {
    744           GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    745                       "Client send malformed `%s' header `%s'\n",
    746                       MHD_HTTP_HEADER_IF_NONE_MATCH,
    747                       etags);
    748         }
    749         else
    750         {
    751           kyp->etag_measure_in = (uint64_t) ev1;
    752           kyp->etag_outcome_in = (uint64_t) ev2;
    753         }
    754       }
    755     } /* etag */
    756 
    757     /* Check access token */
    758     kyp->is_wallet = GNUNET_SYSERR;
    759     qs = TEH_plugin->lookup_h_payto_by_access_token (
    760       TEH_plugin->cls,
    761       &kyp->access_token,
    762       &kyp->h_payto,
    763       &bis_wallet);
    764     if (qs < 0)
    765     {
    766       GNUNET_break (0);
    767       return TALER_MHD_reply_with_ec (
    768         rc->connection,
    769         TALER_EC_GENERIC_DB_FETCH_FAILED,
    770         "lookup_h_payto_by_access_token");
    771     }
    772     if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs)
    773     {
    774       GNUNET_break_op (0);
    775       return TALER_MHD_REPLY_JSON_PACK (
    776         rc->connection,
    777         MHD_HTTP_FORBIDDEN,
    778         TALER_JSON_pack_ec (
    779           TALER_EC_EXCHANGE_KYC_INFO_AUTHORIZATION_FAILED));
    780     }
    781     kyp->is_wallet = (bis_wallet) ? GNUNET_YES : GNUNET_NO;
    782 
    783     if (GNUNET_TIME_absolute_is_future (kyp->timeout))
    784     {
    785       struct TALER_KycCompletedEventP rep = {
    786         .header.size = htons (sizeof (rep)),
    787         .header.type = htons (TALER_DBEVENT_EXCHANGE_KYC_COMPLETED),
    788         .h_payto = kyp->h_payto
    789       };
    790 
    791       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    792                   "Starting DB event listening\n");
    793       kyp->eh = TEH_plugin->event_listen (
    794         TEH_plugin->cls,
    795         GNUNET_TIME_absolute_get_remaining (kyp->timeout),
    796         &rep.header,
    797         &db_event_cb,
    798         rc);
    799     }
    800   } /* end of one-time initialization */
    801 
    802   if (NULL != kyp->response)
    803   {
    804     return MHD_queue_response (rc->connection,
    805                                kyp->response_code,
    806                                kyp->response);
    807   }
    808 
    809   kyp->ru = TALER_EXCHANGEDB_update_rules (TEH_plugin,
    810                                            &TEH_attribute_key,
    811                                            &kyp->h_payto,
    812                                            kyp->is_wallet,
    813                                            &current_rules_cb,
    814                                            kyp);
    815   kyp->suspended = true;
    816   GNUNET_CONTAINER_DLL_insert (kyp_head,
    817                                kyp_tail,
    818                                kyp);
    819   MHD_suspend_connection (rc->connection);
    820   return MHD_YES;
    821 
    822 }
    823 
    824 
    825 /* end of taler-exchange-httpd_kyc-info.c */