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.c (91822B)


      1 /*
      2    This file is part of TALER
      3    Copyright (C) 2014-2025 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.c
     18  * @brief Serve the HTTP interface of the exchange
     19  * @defgroup request Request handling routines
     20  * @author Florian Dold
     21  * @author Benedikt Mueller
     22  * @author Christian Grothoff
     23  */
     24 #include "platform.h"
     25 #include <gnunet/gnunet_util_lib.h>
     26 #include <jansson.h>
     27 #include <microhttpd.h>
     28 #include <sched.h>
     29 #include <sys/resource.h>
     30 #include <limits.h>
     31 #include "taler/taler_kyclogic_lib.h"
     32 #include "taler/taler_templating_lib.h"
     33 #include "taler/taler_mhd_lib.h"
     34 #include "taler-exchange-httpd_post-withdraw.h"
     35 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-accounts.h"
     36 #include \
     37   "taler-exchange-httpd_get-aml-OFFICER_PUB-attributes-H_NORMALIZED_PAYTO.h"
     38 #include "taler-exchange-httpd_post-aml-OFFICER_PUB-decision.h"
     39 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-legitimizations.h"
     40 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-kyc-statistics-NAMES.h"
     41 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-transfers.h"
     42 #include "taler-exchange-httpd_get-aml-OFFICER_PUB-measures.h"
     43 #include "taler-exchange-httpd_post-auditors-AUDITOR_PUB-H_DENOM_PUB.h"
     44 #include "taler-exchange-httpd_post-batch-deposit.h"
     45 #include "taler-exchange-httpd_post-blinding-prepare.h"
     46 #include "taler-exchange-httpd_get-coins-COIN_PUB-history.h"
     47 #include "taler-exchange-httpd_get-config.h"
     48 #include "taler-exchange-httpd_get-contracts-CONTRACT_PUB.h"
     49 #include "taler-exchange-httpd_get-deposits.h"
     50 #include "taler-exchange-httpd_get-keys.h"
     51 #include "taler-exchange-httpd_get-kyc-check-H_NORMALIZED_PAYTO.h"
     52 #include "taler-exchange-httpd_get-kyc-info-ACCESS_TOKEN.h"
     53 #include "taler-exchange-httpd_get-kyc-proof-PROVIDER_NAME.h"
     54 #include "taler-exchange-httpd_post-kyc-start-ID.h"
     55 #include "taler-exchange-httpd_post-kyc-upload-ID.h"
     56 #include "taler-exchange-httpd_post-kyc-wallet.h"
     57 #include "taler-exchange-httpd_kyc-webhook.h"
     58 #include "taler-exchange-httpd_post-aml-OFFICER_PUB-decision.h"
     59 #include "taler-exchange-httpd_management.h"
     60 #include "taler-exchange-httpd_post-melt.h"
     61 #include "taler-exchange-httpd_get-metrics.h"
     62 #include "taler-exchange-httpd_mhd.h"
     63 #include "taler-exchange-httpd_post-purses-PURSE_PUB-create.h"
     64 #include "taler-exchange-httpd_post-purses-PURSE_PUB-deposit.h"
     65 #include "taler-exchange-httpd_get-purses-PURSE_PUB-merge.h"
     66 #include "taler-exchange-httpd_delete-purses-PURSE_PUB.h"
     67 #include "taler-exchange-httpd_post-purses-PURSE_PUB-merge.h"
     68 #include "taler-exchange-httpd_post-recoup-withdraw.h"
     69 #include "taler-exchange-httpd_post-recoup-refresh.h"
     70 #include "taler-exchange-httpd_post-coins-COIN_PUB-refund.h"
     71 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-attest.h"
     72 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-close.h"
     73 #include "taler-exchange-httpd_get-reserves-RESERVE_PUB.h"
     74 #include "taler-exchange-httpd_get-reserves-RESERVE_PUB-attest.h"
     75 #include "taler-exchange-httpd_get-reserves-RESERVE_PUB-history.h"
     76 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-open.h"
     77 #include "taler-exchange-httpd_post-reserves-RESERVE_PUB-purse.h"
     78 #include "taler-exchange-httpd_post-reveal-withdraw.h"
     79 #include "taler-exchange-httpd_post-reveal-melt.h"
     80 #include "taler-exchange-httpd_get-SPA.h"
     81 #include "taler-exchange-httpd_get-TERMS.h"
     82 #include "taler-exchange-httpd_get-transfers-WTID.h"
     83 #include <gnunet/gnunet_mhd_compat.h>
     84 #include "exchange-database/get_exists_aml_officer.h"
     85 #include "exchange-database/preflight.h"
     86 
     87 /**
     88  * Backlog for listen operation on unix domain sockets.
     89  */
     90 #define UNIX_BACKLOG 50
     91 
     92 /**
     93  * How often will we try to connect to the database before giving up?
     94  */
     95 #define MAX_DB_RETRIES 5
     96 
     97 /**
     98  * Above what request latency do we start to log?
     99  */
    100 #define WARN_LATENCY GNUNET_TIME_relative_multiply ( \
    101           GNUNET_TIME_UNIT_MILLISECONDS, 500)
    102 
    103 int TEH_allow_keys_timetravel;
    104 
    105 /**
    106  * Should we allow two HTTPDs to bind to the same port?
    107  */
    108 static int allow_address_reuse;
    109 
    110 /**
    111  * The exchange's configuration (global)
    112  */
    113 const struct GNUNET_CONFIGURATION_Handle *TEH_cfg;
    114 
    115 bool TEH_age_restriction_enabled = false;
    116 
    117 struct TALER_AgeMask TEH_age_restriction_mask = {0};
    118 
    119 /**
    120  * Set to true if we started *any* HTTP daemons.
    121  */
    122 static bool have_daemons;
    123 
    124 struct GNUNET_TIME_Relative TEH_max_keys_caching;
    125 
    126 struct GNUNET_TIME_Relative TEH_reserve_closing_delay;
    127 
    128 struct GNUNET_TIME_Relative TEH_default_p2p_expiration;
    129 
    130 struct GNUNET_TIME_Relative TEH_aml_program_timeout;
    131 
    132 struct TALER_MasterPublicKeyP TEH_master_public_key;
    133 
    134 struct TALER_AttributeEncryptionKeyP TEH_attribute_key;
    135 
    136 struct TALER_EXCHANGEDB_PostgresContext *TEH_pg;
    137 
    138 struct TALER_Amount TEH_stefan_abs;
    139 
    140 struct TALER_Amount TEH_stefan_log;
    141 
    142 struct TALER_Amount TEH_tiny_amount;
    143 
    144 float TEH_stefan_lin;
    145 
    146 json_t *TEH_hard_limits;
    147 
    148 json_t *TEH_zero_limits;
    149 
    150 char *TEH_tos_etag;
    151 
    152 char *TEH_shopping_url;
    153 
    154 char *TEH_aml_spa_dir;
    155 
    156 char *TEH_kyc_spa_dir;
    157 
    158 /**
    159  * Where to redirect users from "/"?
    160  */
    161 static char *toplevel_redirect_url;
    162 
    163 json_t *TEH_global_pdf_form_data;
    164 
    165 char *TEH_currency;
    166 
    167 char *TEH_base_url;
    168 
    169 /**
    170  * Default timeout in seconds for HTTP requests.
    171  */
    172 static unsigned int connection_timeout = 30;
    173 
    174 /**
    175  * -C command-line flag given?
    176  */
    177 static int connection_close;
    178 
    179 /**
    180  * Allows banks to select a custom UI/UX for certain bank-specific
    181  * wallet functions, such as specific texts for buttons based on
    182  * legal requirements.
    183  */
    184 char *TEH_bank_compliance_language;
    185 
    186 /**
    187  * Determines the set of AML forms (and other bank-specific settings)
    188  * to be selected for the AML SPA.
    189  * legal requirements.
    190  */
    191 char *TEH_aml_spa_dialect;
    192 
    193 /**
    194  * Option set to #GNUNET_YES if KYC/AML are enabled.
    195  */
    196 int TEH_enable_kyc;
    197 
    198 /**
    199  * Option set to #GNUNET_YES to disable direct deposits.
    200  */
    201 int TEH_disable_direct_deposit;
    202 
    203 /**
    204  * Option set to #GNUNET_YES to signal to the merchant
    205  * backend that it should swap the terms-of-service and
    206  * KYC auth authentication steps in the user experience.
    207  */
    208 int TEH_kyc_swap_tos_acceptance;
    209 
    210 /**
    211  * -I command-line flag given?
    212  */
    213 int TEH_check_invariants_flag;
    214 
    215 /**
    216  * True if we should commit suicide once all active
    217  * connections are finished.
    218  */
    219 bool TEH_suicide;
    220 
    221 /**
    222  * Value to return from main()
    223  */
    224 static int global_ret;
    225 
    226 /**
    227  * Counter for the number of requests this HTTP has processed so far.
    228  */
    229 static unsigned long long req_count;
    230 
    231 /**
    232  * Counter for the number of open connections.
    233  */
    234 static unsigned long long active_connections;
    235 
    236 /**
    237  * Limit for the number of requests this HTTP may process before restarting.
    238  * (This was added as one way of dealing with unavoidable memory fragmentation
    239  * happening slowly over time.)
    240  */
    241 static unsigned long long req_max;
    242 
    243 /**
    244  * Length of the cspecs array.
    245  */
    246 static unsigned int num_cspecs;
    247 
    248 /**
    249  * Rendering specs for currencies.
    250  */
    251 static struct TALER_CurrencySpecification *cspecs;
    252 
    253 /**
    254  * Rendering spec for our currency.
    255  */
    256 const struct TALER_CurrencySpecification *TEH_cspec;
    257 
    258 
    259 /**
    260  * Context for all CURL operations (useful to the event loop)
    261  */
    262 struct GNUNET_CURL_Context *TEH_curl_ctx;
    263 
    264 /**
    265  * Context for integrating #TEH_curl_ctx with the
    266  * GNUnet event loop.
    267  */
    268 static struct GNUNET_CURL_RescheduleContext *exchange_curl_rc;
    269 
    270 /**
    271  * Signature of functions that handle operations on coins.
    272  *
    273  * @param connection the MHD connection to handle
    274  * @param coin_pub the public key of the coin
    275  * @param root uploaded JSON data
    276  * @return MHD result code
    277  */
    278 typedef enum MHD_Result
    279 (*CoinOpHandler)(struct MHD_Connection *connection,
    280                  const struct TALER_CoinSpendPublicKeyP *coin_pub,
    281                  const json_t *root);
    282 
    283 
    284 /**
    285  * Generate a 404 "not found" reply on @a connection with
    286  * the hint @a details.
    287  *
    288  * @param connection where to send the reply on
    289  * @param details details for the error message, can be NULL
    290  */
    291 static enum MHD_Result
    292 r404 (struct MHD_Connection *connection,
    293       const char *details)
    294 {
    295   return TALER_MHD_reply_with_error (connection,
    296                                      MHD_HTTP_NOT_FOUND,
    297                                      TALER_EC_EXCHANGE_GENERIC_OPERATION_UNKNOWN,
    298                                      details);
    299 }
    300 
    301 
    302 /**
    303  * Handle a "/coins/$COIN_PUB/$OP" POST request.  Parses the "coin_pub"
    304  * EdDSA key of the coin and demultiplexes based on $OP.
    305  *
    306  * @param rc request context
    307  * @param root uploaded JSON data
    308  * @param args array of additional options
    309  * @return MHD result code
    310  */
    311 static enum MHD_Result
    312 handle_post_coins (struct TEH_RequestContext *rc,
    313                    const json_t *root,
    314                    const char *const args[2])
    315 {
    316   struct TALER_CoinSpendPublicKeyP coin_pub;
    317   static const struct
    318   {
    319     /**
    320      * Name of the operation (args[1])
    321      */
    322     const char *op;
    323 
    324     /**
    325      * Function to call to perform the operation.
    326      */
    327     CoinOpHandler handler;
    328 
    329   } h[] = {
    330 #if FIXME_9828
    331     {
    332       .op = "recoup",
    333       .handler = &TEH_handler_recoup
    334     },
    335     {
    336       .op = "recoup-refresh",
    337       .handler = &TEH_handler_recoup_refresh
    338     },
    339 #endif
    340     {
    341       .op = "refund",
    342       .handler = &TEH_handler_refund
    343     },
    344     {
    345       .op = NULL,
    346       .handler = NULL
    347     },
    348   };
    349 
    350   if (GNUNET_OK !=
    351       GNUNET_STRINGS_string_to_data (args[0],
    352                                      strlen (args[0]),
    353                                      &coin_pub,
    354                                      sizeof (coin_pub)))
    355   {
    356     GNUNET_break_op (0);
    357     return TALER_MHD_reply_with_error (rc->connection,
    358                                        MHD_HTTP_BAD_REQUEST,
    359                                        TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
    360                                        args[0]);
    361   }
    362   for (unsigned int i = 0; NULL != h[i].op; i++)
    363     if (0 == strcmp (h[i].op,
    364                      args[1]))
    365       return h[i].handler (rc->connection,
    366                            &coin_pub,
    367                            root);
    368   return r404 (rc->connection,
    369                args[1]);
    370 }
    371 
    372 
    373 /**
    374  * Handle a GET "/coins/$COIN_PUB[/$OP]" request.  Parses the "coin_pub"
    375  * EdDSA key of the coin and demultiplexes based on $OP.
    376  *
    377  * @param rc request context
    378  * @param args array of additional options
    379  * @return MHD result code
    380  */
    381 static enum MHD_Result
    382 handle_get_coins (struct TEH_RequestContext *rc,
    383                   const char *const args[2])
    384 {
    385   struct TALER_CoinSpendPublicKeyP coin_pub;
    386 
    387   if (NULL == args[0])
    388   {
    389     return TALER_MHD_reply_with_error (rc->connection,
    390                                        MHD_HTTP_NOT_FOUND,
    391                                        TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
    392                                        rc->url);
    393   }
    394   if (GNUNET_OK !=
    395       GNUNET_STRINGS_string_to_data (args[0],
    396                                      strlen (args[0]),
    397                                      &coin_pub,
    398                                      sizeof (coin_pub)))
    399   {
    400     GNUNET_break_op (0);
    401     return TALER_MHD_reply_with_error (rc->connection,
    402                                        MHD_HTTP_BAD_REQUEST,
    403                                        TALER_EC_EXCHANGE_GENERIC_COINS_INVALID_COIN_PUB,
    404                                        args[0]);
    405   }
    406   if (NULL != args[1])
    407   {
    408     if (0 == strcmp (args[1],
    409                      "history"))
    410       return TEH_handler_coins_get (rc,
    411                                     &coin_pub);
    412   }
    413   return TALER_MHD_reply_with_error (rc->connection,
    414                                      MHD_HTTP_NOT_FOUND,
    415                                      TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
    416                                      rc->url);
    417 }
    418 
    419 
    420 /**
    421  * Signature of functions that handle operations
    422  * authorized by AML officers.
    423  *
    424  * @param rc request context
    425  * @param officer_pub the public key of the AML officer
    426  * @param root uploaded JSON data
    427  * @return MHD result code
    428  */
    429 typedef enum MHD_Result
    430 (*AmlOpPostHandler)(struct TEH_RequestContext *rc,
    431                     const struct TALER_AmlOfficerPublicKeyP *officer_pub,
    432                     const json_t *root);
    433 
    434 
    435 /**
    436  * Handle a "/aml/$OFFICER_PUB/$OP" POST request.  Parses the "officer_pub"
    437  * EdDSA key of the officer and demultiplexes based on $OP.
    438  *
    439  * @param rc request context
    440  * @param root uploaded JSON data
    441  * @param args array of additional options
    442  * @return MHD result code
    443  */
    444 static enum MHD_Result
    445 handle_post_aml (struct TEH_RequestContext *rc,
    446                  const json_t *root,
    447                  const char *const args[2])
    448 {
    449   struct TALER_AmlOfficerPublicKeyP officer_pub;
    450   static const struct
    451   {
    452     /**
    453      * Name of the operation (args[1])
    454      */
    455     const char *op;
    456 
    457     /**
    458      * Function to call to perform the operation.
    459      */
    460     AmlOpPostHandler handler;
    461 
    462   } h[] = {
    463     {
    464       .op = "decision",
    465       .handler = &TEH_handler_post_aml_decision
    466     },
    467     {
    468       .op = NULL,
    469       .handler = NULL
    470     },
    471   };
    472 
    473   if (GNUNET_OK !=
    474       GNUNET_STRINGS_string_to_data (args[0],
    475                                      strlen (args[0]),
    476                                      &officer_pub,
    477                                      sizeof (officer_pub)))
    478   {
    479     GNUNET_break_op (0);
    480     return TALER_MHD_reply_with_error (rc->connection,
    481                                        MHD_HTTP_BAD_REQUEST,
    482                                        TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
    483                                        args[0]);
    484   }
    485 
    486   {
    487     enum GNUNET_DB_QueryStatus qs;
    488     bool ro;
    489 
    490     qs = TALER_EXCHANGEDB_get_exists_aml_officer (TEH_pg,
    491                                                   &officer_pub,
    492                                                   &ro);
    493     switch (qs)
    494     {
    495     case GNUNET_DB_STATUS_HARD_ERROR:
    496     case GNUNET_DB_STATUS_SOFT_ERROR:
    497       GNUNET_break (0);
    498       return TALER_MHD_reply_with_error (rc->connection,
    499                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    500                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    501                                          NULL);
    502     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    503       GNUNET_break_op (0);
    504       return TALER_MHD_reply_with_error (rc->connection,
    505                                          MHD_HTTP_FORBIDDEN,
    506                                          TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
    507                                          "unknown or inactive");
    508     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    509       break;
    510     }
    511     if (ro)
    512     {
    513       GNUNET_break_op (0);
    514       return TALER_MHD_reply_with_error (rc->connection,
    515                                          MHD_HTTP_CONFLICT,
    516                                          TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
    517                                          "read-only");
    518     }
    519   }
    520 
    521   for (unsigned int i = 0; NULL != h[i].op; i++)
    522     if (0 == strcmp (h[i].op,
    523                      args[1]))
    524       return h[i].handler (rc,
    525                            &officer_pub,
    526                            root);
    527   return r404 (rc->connection,
    528                args[1]);
    529 }
    530 
    531 
    532 /**
    533  * Signature of functions that handle operations
    534  * authorized by AML officers.
    535  *
    536  * @param rc request context
    537  * @param officer_pub the public key of the AML officer
    538  * @param args remaining arguments
    539  * @return MHD result code
    540  */
    541 typedef enum MHD_Result
    542 (*AmlOpGetHandler)(struct TEH_RequestContext *rc,
    543                    const struct TALER_AmlOfficerPublicKeyP *officer_pub,
    544                    const char *const args[]);
    545 
    546 
    547 /**
    548  * Handle a "/aml/$OFFICER_PUB/$OP" GET request.  Parses the "officer_pub"
    549  * EdDSA key of the officer, checks the authentication signature, and
    550  * demultiplexes based on $OP.
    551  *
    552  * @param rc request context
    553  * @param args array of additional options
    554  * @return MHD result code
    555  */
    556 static enum MHD_Result
    557 handle_get_aml (struct TEH_RequestContext *rc,
    558                 const char *const args[])
    559 {
    560   struct TALER_AmlOfficerPublicKeyP officer_pub;
    561   static const struct
    562   {
    563     /**
    564      * Name of the operation (args[1])
    565      */
    566     const char *op;
    567 
    568     /**
    569      * Function to call to perform the operation.
    570      */
    571     AmlOpGetHandler handler;
    572 
    573   } h[] = {
    574     {
    575       .op = "accounts",
    576       .handler = &TEH_handler_aml_accounts_get
    577     },
    578     {
    579       .op = "attributes",
    580       .handler = &TEH_handler_aml_attributes_get
    581     },
    582     {
    583       .op = "decisions",
    584       .handler = &TEH_handler_aml_decisions_get
    585     },
    586     {
    587       .op = "legitimizations",
    588       .handler = &TEH_handler_aml_legitimization_measures_get
    589     },
    590     {
    591       .op = "kyc-statistics",
    592       .handler = &TEH_handler_aml_kyc_statistics_get
    593     },
    594     {
    595       .op = "measures",
    596       .handler = &TEH_handler_aml_measures_get
    597     },
    598     {
    599       .op = "transfers-credit",
    600       .handler = &TEH_handler_aml_transfer_credit_get
    601     },
    602     {
    603       .op = "transfers-kycauth",
    604       .handler = &TEH_handler_aml_transfer_kycauth_get
    605     },
    606     {
    607       .op = "transfers-debit",
    608       .handler = &TEH_handler_aml_transfer_debit_get
    609     },
    610     {
    611       .op = "wallet-credit",
    612       .handler = &TEH_handler_aml_wallet_credit_get
    613     },
    614     {
    615       .op = NULL,
    616       .handler = NULL
    617     },
    618   };
    619 
    620   if (NULL == args[0])
    621   {
    622     GNUNET_break_op (0);
    623     return TALER_MHD_reply_with_error (
    624       rc->connection,
    625       MHD_HTTP_BAD_REQUEST,
    626       TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
    627       "argument missing");
    628   }
    629   if (GNUNET_OK !=
    630       GNUNET_STRINGS_string_to_data (args[0],
    631                                      strlen (args[0]),
    632                                      &officer_pub,
    633                                      sizeof (officer_pub)))
    634   {
    635     GNUNET_break_op (0);
    636     return TALER_MHD_reply_with_error (
    637       rc->connection,
    638       MHD_HTTP_BAD_REQUEST,
    639       TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_PUB_MALFORMED,
    640       args[0]);
    641   }
    642   if (NULL == args[1])
    643   {
    644     GNUNET_break_op (0);
    645     return TALER_MHD_reply_with_error (
    646       rc->connection,
    647       MHD_HTTP_NOT_FOUND,
    648       TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
    649       "AML GET operations must specify an operation identifier");
    650   }
    651 
    652   {
    653     const char *sig_hdr;
    654     struct TALER_AmlOfficerSignatureP officer_sig;
    655 
    656     sig_hdr = MHD_lookup_connection_value (
    657       rc->connection,
    658       MHD_HEADER_KIND,
    659       TALER_AML_OFFICER_SIGNATURE_HEADER);
    660     if ( (NULL == sig_hdr) ||
    661          (GNUNET_OK !=
    662           GNUNET_STRINGS_string_to_data (sig_hdr,
    663                                          strlen (sig_hdr),
    664                                          &officer_sig,
    665                                          sizeof (officer_sig))) )
    666     {
    667       GNUNET_break_op (0);
    668       return TALER_MHD_reply_with_error (
    669         rc->connection,
    670         MHD_HTTP_BAD_REQUEST,
    671         TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
    672         TALER_AML_OFFICER_SIGNATURE_HEADER);
    673     }
    674     if (GNUNET_OK !=
    675         TALER_officer_aml_query_verify (&officer_pub,
    676                                         &officer_sig))
    677     {
    678       GNUNET_break_op (0);
    679       return TALER_MHD_reply_with_error (
    680         rc->connection,
    681         MHD_HTTP_FORBIDDEN,
    682         TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_GET_SIGNATURE_INVALID,
    683         sig_hdr);
    684     }
    685     TEH_METRICS_num_verifications[TEH_MT_SIGNATURE_EDDSA]++;
    686   }
    687 
    688   {
    689     enum GNUNET_DB_QueryStatus qs;
    690     bool ro;
    691 
    692     qs = TALER_EXCHANGEDB_get_exists_aml_officer (TEH_pg,
    693                                                   &officer_pub,
    694                                                   &ro);
    695     switch (qs)
    696     {
    697     case GNUNET_DB_STATUS_HARD_ERROR:
    698     case GNUNET_DB_STATUS_SOFT_ERROR:
    699       GNUNET_break (0);
    700       return TALER_MHD_reply_with_error (rc->connection,
    701                                          MHD_HTTP_INTERNAL_SERVER_ERROR,
    702                                          TALER_EC_GENERIC_DB_FETCH_FAILED,
    703                                          NULL);
    704     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    705       return TALER_MHD_reply_with_error (rc->connection,
    706                                          MHD_HTTP_FORBIDDEN,
    707                                          TALER_EC_EXCHANGE_GENERIC_AML_OFFICER_ACCESS_DENIED,
    708                                          NULL);
    709     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    710       break;
    711     }
    712   }
    713   for (unsigned int i = 0; NULL != h[i].op; i++)
    714     if (0 == strcmp (h[i].op,
    715                      args[1]))
    716       return h[i].handler (rc,
    717                            &officer_pub,
    718                            &args[2]);
    719   return r404 (rc->connection,
    720                args[1]);
    721 }
    722 
    723 
    724 /**
    725  * Signature of functions that handle operations on reserves.
    726  *
    727  * @param rc request context
    728  * @param reserve_pub the public key of the reserve
    729  * @param root uploaded JSON data
    730  * @return MHD result code
    731  */
    732 typedef enum MHD_Result
    733 (*ReserveOpHandler)(struct TEH_RequestContext *rc,
    734                     const struct TALER_ReservePublicKeyP *reserve_pub,
    735                     const json_t *root);
    736 
    737 
    738 /**
    739  * Handle a "/reserves/$RESERVE_PUB/$OP" POST request.  Parses the "reserve_pub"
    740  * EdDSA key of the reserve and demultiplexes based on $OP.
    741  *
    742  * @param rc request context
    743  * @param root uploaded JSON data
    744  * @param args array of additional options
    745  * @return MHD result code
    746  */
    747 static enum MHD_Result
    748 handle_post_reserves (struct TEH_RequestContext *rc,
    749                       const json_t *root,
    750                       const char *const args[2])
    751 {
    752   struct TALER_ReservePublicKeyP reserve_pub;
    753   static const struct
    754   {
    755     /**
    756      * Name of the operation (args[1])
    757      */
    758     const char *op;
    759 
    760     /**
    761      * Function to call to perform the operation.
    762      */
    763     ReserveOpHandler handler;
    764 
    765   } h[] = {
    766     {
    767       .op = "purse",
    768       .handler = &TEH_handler_reserves_purse
    769     },
    770     {
    771       .op = "open",
    772       .handler = &TEH_handler_reserves_open
    773     },
    774     {
    775       .op = "close",
    776       .handler = &TEH_handler_reserves_close
    777     },
    778     {
    779       .op = "attest",
    780       .handler = &TEH_handler_reserves_attest
    781     },
    782     {
    783       .op = NULL,
    784       .handler = NULL
    785     },
    786   };
    787 
    788   if (GNUNET_OK !=
    789       GNUNET_STRINGS_string_to_data (args[0],
    790                                      strlen (args[0]),
    791                                      &reserve_pub,
    792                                      sizeof (reserve_pub)))
    793   {
    794     GNUNET_break_op (0);
    795     return TALER_MHD_reply_with_error (rc->connection,
    796                                        MHD_HTTP_BAD_REQUEST,
    797                                        TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
    798                                        args[0]);
    799   }
    800   for (unsigned int i = 0; NULL != h[i].op; i++)
    801     if (0 == strcmp (h[i].op,
    802                      args[1]))
    803       return h[i].handler (rc,
    804                            &reserve_pub,
    805                            root);
    806   return r404 (rc->connection,
    807                args[1]);
    808 }
    809 
    810 
    811 /**
    812  * Signature of functions that handle GET operations on reserves.
    813  *
    814  * @param rc request context
    815  * @param reserve_pub the public key of the reserve
    816  * @return MHD result code
    817  */
    818 typedef enum MHD_Result
    819 (*ReserveGetOpHandler)(struct TEH_RequestContext *rc,
    820                        const struct TALER_ReservePublicKeyP *reserve_pub);
    821 
    822 
    823 /**
    824  * Handle a "GET /reserves/$RESERVE_PUB[/$OP]" request.  Parses the "reserve_pub"
    825  * EdDSA key of the reserve and demultiplexes based on $OP.
    826  *
    827  * @param rc request context
    828  * @param args NULL-terminated array of additional options, zero, one or two
    829  * @return MHD result code
    830  */
    831 static enum MHD_Result
    832 handle_get_reserves (struct TEH_RequestContext *rc,
    833                      const char *const args[])
    834 {
    835   struct TALER_ReservePublicKeyP reserve_pub;
    836   static const struct
    837   {
    838     /**
    839      * Name of the operation (args[1]), optional
    840      */
    841     const char *op;
    842 
    843     /**
    844      * Function to call to perform the operation.
    845      */
    846     ReserveGetOpHandler handler;
    847 
    848   } h[] = {
    849     {
    850       .op = NULL,
    851       .handler = &TEH_handler_reserves_get
    852     },
    853     {
    854       .op = "history",
    855       .handler = &TEH_handler_reserves_history
    856     },
    857     {
    858       .op = "attest",
    859       .handler = &TEH_handler_get_reserves_attest
    860     },
    861     {
    862       .op = NULL,
    863       .handler = NULL
    864     },
    865   };
    866 
    867   if ( (NULL == args[0]) ||
    868        (GNUNET_OK !=
    869         GNUNET_STRINGS_string_to_data (args[0],
    870                                        strlen (args[0]),
    871                                        &reserve_pub,
    872                                        sizeof (reserve_pub))) )
    873   {
    874     GNUNET_break_op (0);
    875     return TALER_MHD_reply_with_error (rc->connection,
    876                                        MHD_HTTP_BAD_REQUEST,
    877                                        TALER_EC_GENERIC_RESERVE_PUB_MALFORMED,
    878                                        args[0]);
    879   }
    880   for (unsigned int i = 0; NULL != h[i].handler; i++)
    881   {
    882     if ( ( (NULL == args[1]) &&
    883            (NULL == h[i].op) ) ||
    884          ( (NULL != args[1]) &&
    885            (NULL != h[i].op) &&
    886            (0 == strcmp (h[i].op,
    887                          args[1])) ) )
    888       return h[i].handler (rc,
    889                            &reserve_pub);
    890   }
    891   return r404 (rc->connection,
    892                args[1]);
    893 }
    894 
    895 
    896 /**
    897  * Signature of functions that handle operations on purses.
    898  *
    899  * @param rc request handle
    900  * @param purse_pub the public key of the purse
    901  * @param root uploaded JSON data
    902  * @return MHD result code
    903  */
    904 typedef enum MHD_Result
    905 (*PurseOpHandler)(struct TEH_RequestContext *rc,
    906                   const struct TALER_PurseContractPublicKeyP *purse_pub,
    907                   const json_t *root);
    908 
    909 
    910 /**
    911  * Handle a "/purses/$RESERVE_PUB/$OP" POST request.  Parses the "purse_pub"
    912  * EdDSA key of the purse and demultiplexes based on $OP.
    913  *
    914  * @param rc request context
    915  * @param root uploaded JSON data
    916  * @param args array of additional options
    917  * @return MHD result code
    918  */
    919 static enum MHD_Result
    920 handle_post_purses (struct TEH_RequestContext *rc,
    921                     const json_t *root,
    922                     const char *const args[2])
    923 {
    924   struct TALER_PurseContractPublicKeyP purse_pub;
    925   static const struct
    926   {
    927     /**
    928      * Name of the operation (args[1])
    929      */
    930     const char *op;
    931 
    932     /**
    933      * Function to call to perform the operation.
    934      */
    935     PurseOpHandler handler;
    936 
    937   } h[] = {
    938     {
    939       .op = "create",
    940       .handler = &TEH_handler_purses_create
    941     },
    942     {
    943       .op = "deposit",
    944       .handler = &TEH_handler_purses_deposit
    945     },
    946     {
    947       .op = "merge",
    948       .handler = &TEH_handler_purses_merge
    949     },
    950     {
    951       .op = NULL,
    952       .handler = NULL
    953     },
    954   };
    955 
    956   if (GNUNET_OK !=
    957       GNUNET_STRINGS_string_to_data (args[0],
    958                                      strlen (args[0]),
    959                                      &purse_pub,
    960                                      sizeof (purse_pub)))
    961   {
    962     GNUNET_break_op (0);
    963     return TALER_MHD_reply_with_error (rc->connection,
    964                                        MHD_HTTP_BAD_REQUEST,
    965                                        TALER_EC_EXCHANGE_GENERIC_PURSE_PUB_MALFORMED,
    966                                        args[0]);
    967   }
    968   for (unsigned int i = 0; NULL != h[i].op; i++)
    969     if (0 == strcmp (h[i].op,
    970                      args[1]))
    971       return h[i].handler (rc,
    972                            &purse_pub,
    973                            root);
    974   return r404 (rc->connection,
    975                args[1]);
    976 }
    977 
    978 
    979 /**
    980  * Increments our request counter and checks if this
    981  * process should commit suicide.
    982  */
    983 static void
    984 check_suicide (void)
    985 {
    986   pid_t chld;
    987   unsigned long long cnt;
    988 
    989   cnt = req_count++;
    990   if (req_max != cnt)
    991     return;
    992   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    993               "Restarting exchange service after %llu requests\n",
    994               cnt);
    995   /* Stop accepting new connections */
    996   TALER_MHD_daemons_quiesce ();
    997   /* Continue handling existing connections in child,
    998      so that this process can die and be replaced by
    999      systemd with a fresh one */
   1000   chld = fork ();
   1001   if (-1 == chld)
   1002   {
   1003     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   1004                          "fork");
   1005     _exit (1);
   1006   }
   1007   if (0 != chld)
   1008   {
   1009     /* We are the parent, instant-suicide! */
   1010     _exit (0);
   1011   }
   1012   TEH_suicide = true;
   1013 }
   1014 
   1015 
   1016 /**
   1017  * Function called whenever MHD is done with a request.  If the
   1018  * request was a POST, we may have stored a `struct Buffer *` in the
   1019  * @a con_cls that might still need to be cleaned up.  Call the
   1020  * respective function to free the memory.
   1021  *
   1022  * @param cls client-defined closure
   1023  * @param connection connection handle
   1024  * @param con_cls value as set by the last call to
   1025  *        the #MHD_AccessHandlerCallback
   1026  * @param toe reason for request termination
   1027  * @see #MHD_OPTION_NOTIFY_COMPLETED
   1028  * @ingroup request
   1029  */
   1030 static void
   1031 handle_mhd_completion_callback (void *cls,
   1032                                 struct MHD_Connection *connection,
   1033                                 void **con_cls,
   1034                                 enum MHD_RequestTerminationCode toe)
   1035 {
   1036   struct TEH_RequestContext *rc = *con_cls;
   1037   struct GNUNET_AsyncScopeSave old_scope;
   1038 
   1039   (void) cls;
   1040   if (NULL == rc)
   1041     return;
   1042   GNUNET_async_scope_enter (&rc->async_scope_id,
   1043                             &old_scope);
   1044   check_suicide ();
   1045   TEH_check_invariants ();
   1046   if (NULL != rc->rh_cleaner)
   1047     rc->rh_cleaner (rc);
   1048   if (NULL != rc->root)
   1049   {
   1050     json_decref (rc->root);
   1051     rc->root = NULL;
   1052   }
   1053   TEH_check_invariants ();
   1054   {
   1055 #if MHD_VERSION >= 0x00097304
   1056     const union MHD_ConnectionInfo *ci;
   1057     unsigned int http_status = 0;
   1058 
   1059     ci = MHD_get_connection_info (connection,
   1060                                   MHD_CONNECTION_INFO_HTTP_STATUS);
   1061     if (NULL != ci)
   1062       http_status = ci->http_status;
   1063     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1064                 "Request for `%s' completed with HTTP status %u (%d)\n",
   1065                 rc->url,
   1066                 http_status,
   1067                 toe);
   1068 #else
   1069     (void) connection;
   1070     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1071                 "Request for `%s' completed (%d)\n",
   1072                 rc->url,
   1073                 toe);
   1074 #endif
   1075   }
   1076 
   1077   TALER_MHD_parse_post_cleanup_callback (rc->opaque_post_parsing_context);
   1078   /* Sanity-check that we didn't leave any transactions hanging */
   1079   GNUNET_break (GNUNET_OK ==
   1080                 TALER_EXCHANGEDB_preflight (TEH_pg));
   1081   {
   1082     struct GNUNET_TIME_Relative latency;
   1083 
   1084     latency = GNUNET_TIME_absolute_get_duration (rc->start_time);
   1085     if (latency.rel_value_us >
   1086         WARN_LATENCY.rel_value_us)
   1087       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1088                   "Request for `%s' took %s\n",
   1089                   rc->url,
   1090                   GNUNET_STRINGS_relative_time_to_string (latency,
   1091                                                           GNUNET_YES));
   1092   }
   1093   GNUNET_free (rc);
   1094   *con_cls = NULL;
   1095   GNUNET_async_scope_restore (&old_scope);
   1096 }
   1097 
   1098 
   1099 /**
   1100  * We found a request handler responsible for handling a request. Parse the
   1101  * @a upload_data (if applicable) and the @a url and call the
   1102  * handler.
   1103  *
   1104  * @param rc request context
   1105  * @param url rest of the URL to parse
   1106  * @param upload_data upload data to parse (if available)
   1107  * @param[in,out] upload_data_size number of bytes in @a upload_data
   1108  * @return MHD result code
   1109  */
   1110 static enum MHD_Result
   1111 proceed_with_handler (struct TEH_RequestContext *rc,
   1112                       const char *url,
   1113                       const char *upload_data,
   1114                       size_t *upload_data_size)
   1115 {
   1116   const struct TEH_RequestHandler *rh = rc->rh;
   1117   const char *args[rh->nargs + 2];
   1118   size_t ulen = strlen (url) + 1;
   1119   enum MHD_Result ret;
   1120 
   1121   /* We do check for "ulen" here, because we'll later stack-allocate a buffer
   1122      of that size and don't want to enable malicious clients to cause us
   1123      huge stack allocations. */
   1124   if (ulen > 512)
   1125   {
   1126     /* 512 is simply "big enough", as it is bigger than "6 * 54",
   1127        which is the longest URL format we ever get (for
   1128        /deposits/).  The value should be adjusted if we ever define protocol
   1129        endpoints with plausibly longer inputs.  */
   1130     GNUNET_break_op (0);
   1131     return TALER_MHD_reply_with_error (rc->connection,
   1132                                        MHD_HTTP_URI_TOO_LONG,
   1133                                        TALER_EC_GENERIC_URI_TOO_LONG,
   1134                                        url);
   1135   }
   1136 
   1137   /* All POST endpoints come with a body in JSON format. So we parse
   1138      the JSON here. */
   1139   if ( (0 == strcasecmp (rh->method,
   1140                          MHD_HTTP_METHOD_POST)) &&
   1141        (NULL == rc->root) )
   1142   {
   1143     enum GNUNET_GenericReturnValue res;
   1144 
   1145     res = TALER_MHD_parse_post_json (rc->connection,
   1146                                      &rc->opaque_post_parsing_context,
   1147                                      upload_data,
   1148                                      upload_data_size,
   1149                                      &rc->root);
   1150     if (GNUNET_SYSERR == res)
   1151     {
   1152       GNUNET_assert (NULL == rc->root);
   1153       return MHD_NO; /* bad upload, could not even generate error */
   1154     }
   1155     if ( (GNUNET_NO == res) ||
   1156          (NULL == rc->root) )
   1157     {
   1158       GNUNET_assert (NULL == rc->root);
   1159       return MHD_YES; /* so far incomplete upload or parser error */
   1160     }
   1161   }
   1162 
   1163   {
   1164     char d[ulen];
   1165     unsigned int i;
   1166     char *sp;
   1167 
   1168     /* Parse command-line arguments */
   1169     /* make a copy of 'url' because 'strtok_r()' will modify */
   1170     GNUNET_memcpy (d,
   1171                    url,
   1172                    ulen);
   1173     i = 0;
   1174     args[i++] = strtok_r (d, "/", &sp);
   1175     while ( (NULL != args[i - 1]) &&
   1176             (i <= rh->nargs + 1) )
   1177       args[i++] = strtok_r (NULL, "/", &sp);
   1178     /* make sure above loop ran nicely until completion, and also
   1179        that there is no excess data in 'd' afterwards */
   1180     if ( ( (rh->nargs_is_upper_bound) &&
   1181            (i - 1 > rh->nargs) ) ||
   1182          ( (! rh->nargs_is_upper_bound) &&
   1183            (i - 1 != rh->nargs) ) )
   1184     {
   1185       char emsg[128 + 512];
   1186 
   1187       GNUNET_snprintf (emsg,
   1188                        sizeof (emsg),
   1189                        "Got %u+/%u segments for `%s' request (`%s')",
   1190                        i - 1,
   1191                        rh->nargs,
   1192                        rh->url,
   1193                        url);
   1194       GNUNET_break_op (0);
   1195       return TALER_MHD_reply_with_error (rc->connection,
   1196                                          MHD_HTTP_NOT_FOUND,
   1197                                          TALER_EC_EXCHANGE_GENERIC_WRONG_NUMBER_OF_SEGMENTS,
   1198                                          emsg);
   1199     }
   1200     GNUNET_assert (NULL == args[i - 1]);
   1201 
   1202     /* Above logic ensures that 'root' is exactly non-NULL for POST operations,
   1203        so we test for 'root' to decide which handler to invoke. */
   1204     if (0 == strcasecmp (rh->method,
   1205                          MHD_HTTP_METHOD_POST))
   1206       ret = rh->handler.post (rc,
   1207                               rc->root,
   1208                               args);
   1209     else if (0 == strcasecmp (rh->method,
   1210                               MHD_HTTP_METHOD_DELETE))
   1211       ret = rh->handler.delete (rc,
   1212                                 args);
   1213     else /* Only GET left */
   1214       ret = rh->handler.get (rc,
   1215                              args);
   1216   }
   1217   return ret;
   1218 }
   1219 
   1220 
   1221 /**
   1222  * Handle a "/seed" request.
   1223  *
   1224  * @param rc request context
   1225  * @param args array of additional options (must be empty for this function)
   1226  * @return MHD result code
   1227  */
   1228 static enum MHD_Result
   1229 handler_seed (struct TEH_RequestContext *rc,
   1230               const char *const args[])
   1231 {
   1232 #define SEED_SIZE 32
   1233   char *body;
   1234   enum MHD_Result ret;
   1235   struct MHD_Response *resp;
   1236 
   1237   (void) args;
   1238   body = malloc (SEED_SIZE); /* must use malloc(), because MHD will use free() */
   1239   if (NULL == body)
   1240     return MHD_NO;
   1241   GNUNET_CRYPTO_random_block (body,
   1242                               SEED_SIZE);
   1243   resp = MHD_create_response_from_buffer (SEED_SIZE,
   1244                                           body,
   1245                                           MHD_RESPMEM_MUST_FREE);
   1246   TALER_MHD_add_global_headers (resp,
   1247                                 false);
   1248   ret = MHD_queue_response (rc->connection,
   1249                             MHD_HTTP_OK,
   1250                             resp);
   1251   GNUNET_break (MHD_YES == ret);
   1252   MHD_destroy_response (resp);
   1253   return ret;
   1254 #undef SEED_SIZE
   1255 }
   1256 
   1257 
   1258 /**
   1259  * Signature of functions that handle simple
   1260  * POST operations for the management API.
   1261  *
   1262  * @param connection the MHD connection to handle
   1263  * @param root uploaded JSON data
   1264  * @return MHD result code
   1265  */
   1266 typedef enum MHD_Result
   1267 (*ManagementPostHandler)(
   1268   struct MHD_Connection *connection,
   1269   const json_t *root);
   1270 
   1271 
   1272 /**
   1273  * Handle POST "/management/..." requests.
   1274  *
   1275  * @param rc request context
   1276  * @param root uploaded JSON data
   1277  * @param args array of additional options
   1278  * @return MHD result code
   1279  */
   1280 static enum MHD_Result
   1281 handle_post_management (struct TEH_RequestContext *rc,
   1282                         const json_t *root,
   1283                         const char *const args[])
   1284 {
   1285   static const struct
   1286   {
   1287     const char *arg0;
   1288     const char *arg1;
   1289     ManagementPostHandler handler;
   1290   } plain_posts[] = {
   1291     {
   1292       .arg0 = "keys",
   1293       .handler = &TEH_handler_management_post_keys
   1294     },
   1295     {
   1296       .arg0 = "wire",
   1297       .handler = &TEH_handler_management_post_wire
   1298     },
   1299     {
   1300       .arg0 = "wire",
   1301       .arg1 = "disable",
   1302       .handler = &TEH_handler_management_post_wire_disable
   1303     },
   1304     {
   1305       .arg0 = "wire-fee",
   1306       .handler = &TEH_handler_management_post_wire_fees
   1307     },
   1308     {
   1309       .arg0 = "global-fee",
   1310       .handler = &TEH_handler_management_post_global_fees
   1311     },
   1312     {
   1313       .arg0 = "drain",
   1314       .handler = &TEH_handler_management_post_drain
   1315     },
   1316     {
   1317       .arg0 = "aml-officers",
   1318       .handler = &TEH_handler_management_aml_officers
   1319     },
   1320     {
   1321       .arg0 = "partners",
   1322       .handler = &TEH_handler_management_partners
   1323     },
   1324     {
   1325       NULL,
   1326       NULL,
   1327       NULL
   1328     }
   1329   };
   1330   if (NULL == args[0])
   1331   {
   1332     GNUNET_break_op (0);
   1333     return r404 (rc->connection,
   1334                  "/management");
   1335   }
   1336   if (0 == strcmp (args[0],
   1337                    "auditors"))
   1338   {
   1339     struct TALER_AuditorPublicKeyP auditor_pub;
   1340 
   1341     if (NULL == args[1])
   1342       return TEH_handler_management_auditors (rc->connection,
   1343                                               root);
   1344     if ( (NULL == args[1]) ||
   1345          (NULL == args[2]) ||
   1346          (0 != strcmp (args[2],
   1347                        "disable")) ||
   1348          (NULL != args[3]) )
   1349       return r404 (rc->connection,
   1350                    "/management/auditors/$AUDITOR_PUB/disable");
   1351     if (GNUNET_OK !=
   1352         GNUNET_STRINGS_string_to_data (args[1],
   1353                                        strlen (args[1]),
   1354                                        &auditor_pub,
   1355                                        sizeof (auditor_pub)))
   1356     {
   1357       GNUNET_break_op (0);
   1358       return TALER_MHD_reply_with_error (rc->connection,
   1359                                          MHD_HTTP_BAD_REQUEST,
   1360                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1361                                          args[1]);
   1362     }
   1363     return TEH_handler_management_auditors_AP_disable (rc->connection,
   1364                                                        &auditor_pub,
   1365                                                        root);
   1366   }
   1367   if (0 == strcmp (args[0],
   1368                    "denominations"))
   1369   {
   1370     struct TALER_DenominationHashP h_denom_pub;
   1371 
   1372     if ( (NULL == args[0]) ||
   1373          (NULL == args[1]) ||
   1374          (NULL == args[2]) ||
   1375          (0 != strcmp (args[2],
   1376                        "revoke")) ||
   1377          (NULL != args[3]) )
   1378       return r404 (rc->connection,
   1379                    "/management/denominations/$HDP/revoke");
   1380     if (GNUNET_OK !=
   1381         GNUNET_STRINGS_string_to_data (args[1],
   1382                                        strlen (args[1]),
   1383                                        &h_denom_pub,
   1384                                        sizeof (h_denom_pub)))
   1385     {
   1386       GNUNET_break_op (0);
   1387       return TALER_MHD_reply_with_error (rc->connection,
   1388                                          MHD_HTTP_BAD_REQUEST,
   1389                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1390                                          args[1]);
   1391     }
   1392     return TEH_handler_management_denominations_HDP_revoke (rc->connection,
   1393                                                             &h_denom_pub,
   1394                                                             root);
   1395   }
   1396   if (0 == strcmp (args[0],
   1397                    "signkeys"))
   1398   {
   1399     struct TALER_ExchangePublicKeyP exchange_pub;
   1400 
   1401     if ( (NULL == args[0]) ||
   1402          (NULL == args[1]) ||
   1403          (NULL == args[2]) ||
   1404          (0 != strcmp (args[2],
   1405                        "revoke")) ||
   1406          (NULL != args[3]) )
   1407       return r404 (rc->connection,
   1408                    "/management/signkeys/$HDP/revoke");
   1409     if (GNUNET_OK !=
   1410         GNUNET_STRINGS_string_to_data (args[1],
   1411                                        strlen (args[1]),
   1412                                        &exchange_pub,
   1413                                        sizeof (exchange_pub)))
   1414     {
   1415       GNUNET_break_op (0);
   1416       return TALER_MHD_reply_with_error (rc->connection,
   1417                                          MHD_HTTP_BAD_REQUEST,
   1418                                          TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1419                                          args[1]);
   1420     }
   1421     return TEH_handler_management_signkeys_EP_revoke (rc->connection,
   1422                                                       &exchange_pub,
   1423                                                       root);
   1424   }
   1425   for (unsigned int i = 0;
   1426        NULL != plain_posts[i].handler;
   1427        i++)
   1428   {
   1429     if (0 == strcmp (args[0],
   1430                      plain_posts[i].arg0))
   1431     {
   1432       if ( ( (NULL == args[1]) &&
   1433              (NULL == plain_posts[i].arg1) ) ||
   1434            ( (NULL != args[1]) &&
   1435              (NULL != plain_posts[i].arg1) &&
   1436              (0 == strcmp (args[1],
   1437                            plain_posts[i].arg1)) ) )
   1438         return plain_posts[i].handler (rc->connection,
   1439                                        root);
   1440     }
   1441   }
   1442   GNUNET_break_op (0);
   1443   return r404 (rc->connection,
   1444                "/management/*");
   1445 }
   1446 
   1447 
   1448 /**
   1449  * Handle a GET "/management" request.
   1450  *
   1451  * @param rc request context
   1452  * @param args array of additional options (must be [0] == "keys")
   1453  * @return MHD result code
   1454  */
   1455 static enum MHD_Result
   1456 handle_get_management (struct TEH_RequestContext *rc,
   1457                        const char *const args[2])
   1458 {
   1459   if ( (NULL != args[0]) &&
   1460        (0 == strcmp (args[0],
   1461                      "keys")) &&
   1462        (NULL == args[1]) )
   1463   {
   1464     return TEH_keys_management_get_keys_handler (rc->rh,
   1465                                                  rc->connection);
   1466   }
   1467   GNUNET_break_op (0);
   1468   return r404 (rc->connection,
   1469                "/management/*");
   1470 }
   1471 
   1472 
   1473 /**
   1474  * Handle POST "/auditors/..." requests.
   1475  *
   1476  * @param rc request context
   1477  * @param root uploaded JSON data
   1478  * @param args array of additional options
   1479  * @return MHD result code
   1480  */
   1481 static enum MHD_Result
   1482 handle_post_auditors (struct TEH_RequestContext *rc,
   1483                       const json_t *root,
   1484                       const char *const args[])
   1485 {
   1486   struct TALER_AuditorPublicKeyP auditor_pub;
   1487   struct TALER_DenominationHashP h_denom_pub;
   1488 
   1489   if ( (NULL == args[0]) ||
   1490        (NULL == args[1]) ||
   1491        (NULL != args[2]) )
   1492   {
   1493     GNUNET_break_op (0);
   1494     return r404 (rc->connection,
   1495                  "/auditors/$AUDITOR_PUB/$H_DENOM_PUB");
   1496   }
   1497 
   1498   if (GNUNET_OK !=
   1499       GNUNET_STRINGS_string_to_data (args[0],
   1500                                      strlen (args[0]),
   1501                                      &auditor_pub,
   1502                                      sizeof (auditor_pub)))
   1503   {
   1504     GNUNET_break_op (0);
   1505     return TALER_MHD_reply_with_error (rc->connection,
   1506                                        MHD_HTTP_BAD_REQUEST,
   1507                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1508                                        args[0]);
   1509   }
   1510   if (GNUNET_OK !=
   1511       GNUNET_STRINGS_string_to_data (args[1],
   1512                                      strlen (args[1]),
   1513                                      &h_denom_pub,
   1514                                      sizeof (h_denom_pub)))
   1515   {
   1516     GNUNET_break_op (0);
   1517     return TALER_MHD_reply_with_error (rc->connection,
   1518                                        MHD_HTTP_BAD_REQUEST,
   1519                                        TALER_EC_GENERIC_PARAMETER_MALFORMED,
   1520                                        args[1]);
   1521   }
   1522   return TEH_handler_auditors (rc->connection,
   1523                                &auditor_pub,
   1524                                &h_denom_pub,
   1525                                root);
   1526 }
   1527 
   1528 
   1529 /**
   1530  * Generates the response for "/", redirecting the
   1531  * client to the ``toplevel_redirect_url``.
   1532  *
   1533  * @param rc request context
   1534  * @param args remaining arguments (should be empty)
   1535  * @return MHD result code
   1536  */
   1537 static enum MHD_Result
   1538 toplevel_redirect (struct TEH_RequestContext *rc,
   1539                    const char *const args[])
   1540 {
   1541   char *text;
   1542   struct MHD_Response *response;
   1543 
   1544   GNUNET_asprintf (&text,
   1545                    "Redirecting to %s",
   1546                    toplevel_redirect_url);
   1547   response = MHD_create_response_from_buffer (strlen (text),
   1548                                               text,
   1549                                               MHD_RESPMEM_MUST_COPY);
   1550   GNUNET_free (text);
   1551   if (NULL == response)
   1552   {
   1553     GNUNET_break (0);
   1554     return MHD_NO;
   1555   }
   1556   TALER_MHD_add_global_headers (response,
   1557                                 true);
   1558   GNUNET_break (MHD_YES ==
   1559                 MHD_add_response_header (response,
   1560                                          MHD_HTTP_HEADER_CONTENT_TYPE,
   1561                                          "text/plain"));
   1562   if (MHD_NO ==
   1563       MHD_add_response_header (response,
   1564                                MHD_HTTP_HEADER_LOCATION,
   1565                                toplevel_redirect_url))
   1566   {
   1567     GNUNET_break (0);
   1568     MHD_destroy_response (response);
   1569     return MHD_NO;
   1570   }
   1571 
   1572   {
   1573     enum MHD_Result ret;
   1574 
   1575     ret = MHD_queue_response (rc->connection,
   1576                               MHD_HTTP_FOUND,
   1577                               response);
   1578     MHD_destroy_response (response);
   1579     return ret;
   1580   }
   1581 }
   1582 
   1583 
   1584 /**
   1585  * Handle incoming HTTP request.
   1586  *
   1587  * @param cls closure for MHD daemon (unused)
   1588  * @param connection the connection
   1589  * @param url the requested url
   1590  * @param method the method (POST, GET, ...)
   1591  * @param version HTTP version (ignored)
   1592  * @param upload_data request data
   1593  * @param upload_data_size size of @a upload_data in bytes
   1594  * @param con_cls closure for request (a `struct TEH_RequestContext *`)
   1595  * @return MHD result code
   1596  */
   1597 static enum MHD_Result
   1598 handle_mhd_request (void *cls,
   1599                     struct MHD_Connection *connection,
   1600                     const char *url,
   1601                     const char *method,
   1602                     const char *version,
   1603                     const char *upload_data,
   1604                     size_t *upload_data_size,
   1605                     void **con_cls)
   1606 {
   1607   static struct TEH_RequestHandler handlers[] = {
   1608     /* /robots.txt: disallow everything */
   1609     {
   1610       .url = "robots.txt",
   1611       .method = MHD_HTTP_METHOD_GET,
   1612       .handler.get = &TEH_handler_static_response,
   1613       .mime_type = "text/plain",
   1614       .data = "User-agent: *\nDisallow: /\n",
   1615       .response_code = MHD_HTTP_OK
   1616     },
   1617     /* Landing page, redirect to toplevel_redirect_url */
   1618     {
   1619       .url = "",
   1620       .method = MHD_HTTP_METHOD_GET,
   1621       .handler.get = &toplevel_redirect
   1622     },
   1623     /* AGPL licensing page, redirect to source. As per the AGPL-license, every
   1624        deployment is required to offer the user a download of the source of
   1625        the actual deployment. We make this easy by including a redirect to the
   1626        source here. */
   1627     {
   1628       .url = "agpl",
   1629       .method = MHD_HTTP_METHOD_GET,
   1630       .handler.get = &TEH_handler_agpl_redirect
   1631     },
   1632     {
   1633       .url = "seed",
   1634       .method = MHD_HTTP_METHOD_GET,
   1635       .handler.get = &handler_seed
   1636     },
   1637     /* Configuration */
   1638     {
   1639       .url = "config",
   1640       .method = MHD_HTTP_METHOD_GET,
   1641       .handler.get = &TEH_handler_config
   1642     },
   1643     /* Performance metrics */
   1644     {
   1645       .url = "metrics",
   1646       .method = MHD_HTTP_METHOD_GET,
   1647       .handler.get = &TEH_handler_metrics
   1648     },
   1649     /* Terms of service */
   1650     {
   1651       .url = "terms",
   1652       .method = MHD_HTTP_METHOD_GET,
   1653       .handler.get = &TEH_handler_terms
   1654     },
   1655     /* Privacy policy */
   1656     {
   1657       .url = "privacy",
   1658       .method = MHD_HTTP_METHOD_GET,
   1659       .handler.get = &TEH_handler_privacy
   1660     },
   1661     /* Return key material and fundamental properties for this exchange */
   1662     {
   1663       .url = "keys",
   1664       .method = MHD_HTTP_METHOD_GET,
   1665       .handler.get = &TEH_keys_get_handler,
   1666     },
   1667     {
   1668       .url = "batch-deposit",
   1669       .method = MHD_HTTP_METHOD_POST,
   1670       .handler.post = &TEH_handler_batch_deposit,
   1671       .nargs = 0
   1672     },
   1673     /* request R's input for Clause-Schnorr signatures in batches */
   1674     {
   1675       .url = "blinding-prepare",
   1676       .method = MHD_HTTP_METHOD_POST,
   1677       .handler.post = &TEH_handler_blinding_prepare,
   1678       .nargs = 0
   1679     },
   1680     /* withdraw request, available since v26 of the API */
   1681     {
   1682       .url = "withdraw",
   1683       .method = MHD_HTTP_METHOD_POST,
   1684       .handler.post = &TEH_handler_withdraw,
   1685       .nargs = 0
   1686     },
   1687     {
   1688       .url = "reserves",
   1689       .method = MHD_HTTP_METHOD_GET,
   1690       .handler.get = &handle_get_reserves,
   1691       .nargs = 2,
   1692       .nargs_is_upper_bound = true
   1693     },
   1694     {
   1695       .url = "reserves",
   1696       .method = MHD_HTTP_METHOD_POST,
   1697       .handler.post = &handle_post_reserves,
   1698       .nargs = 2
   1699     },
   1700     /* reveal-withdraw operation, introduced with v26 */
   1701     {
   1702       .url = "reveal-withdraw",
   1703       .method = MHD_HTTP_METHOD_POST,
   1704       .handler.post = &TEH_handler_reveal_withdraw,
   1705       .nargs = 0
   1706     },
   1707     /* reveal-melt operation, introduced with v27 */
   1708     {
   1709       .url = "reveal-melt",
   1710       .method = MHD_HTTP_METHOD_POST,
   1711       .handler.post = &TEH_handler_reveal_melt,
   1712       .nargs = 0
   1713     },
   1714     /* coins */
   1715     {
   1716       .url = "coins",
   1717       .method = MHD_HTTP_METHOD_POST,
   1718       .handler.post = &handle_post_coins,
   1719       .nargs = 2
   1720     },
   1721     {
   1722       .url = "coins",
   1723       .method = MHD_HTTP_METHOD_GET,
   1724       .handler.get = &handle_get_coins,
   1725       .nargs = 2,
   1726       .nargs_is_upper_bound = true
   1727     },
   1728     /* melting operation, introduced with v26 */
   1729     {
   1730       .url = "melt",
   1731       .method = MHD_HTTP_METHOD_POST,
   1732       .handler.post = &TEH_handler_melt,
   1733       .nargs = 0
   1734     },
   1735     /* tracking transfers */
   1736     {
   1737       .url = "transfers",
   1738       .method = MHD_HTTP_METHOD_GET,
   1739       .handler.get = &TEH_handler_transfers_get,
   1740       .nargs = 1
   1741     },
   1742     /* tracking deposits */
   1743     {
   1744       .url = "deposits",
   1745       .method = MHD_HTTP_METHOD_GET,
   1746       .handler.get = &TEH_handler_deposits_get,
   1747       .nargs = 4
   1748     },
   1749     /* Operating on purses */
   1750     {
   1751       .url = "purses",
   1752       .method = MHD_HTTP_METHOD_POST,
   1753       .handler.post = &handle_post_purses,
   1754       .nargs = 2
   1755     },
   1756     /* Getting purse status */
   1757     {
   1758       .url = "purses",
   1759       .method = MHD_HTTP_METHOD_GET,
   1760       .handler.get = &TEH_handler_purses_get,
   1761       .nargs = 2
   1762     },
   1763     /* Deleting purse */
   1764     {
   1765       .url = "purses",
   1766       .method = MHD_HTTP_METHOD_DELETE,
   1767       .handler.delete = &TEH_handler_purses_delete,
   1768       .nargs = 1
   1769     },
   1770     /* Getting contracts */
   1771     {
   1772       .url = "contracts",
   1773       .method = MHD_HTTP_METHOD_GET,
   1774       .handler.get = &TEH_handler_contracts_get,
   1775       .nargs = 1
   1776     },
   1777     /* KYC endpoints */
   1778     {
   1779       .url = "kyc-check",
   1780       .method = MHD_HTTP_METHOD_GET,
   1781       .handler.get = &TEH_handler_kyc_check,
   1782       .nargs = 1
   1783     },
   1784     {
   1785       .url = "kyc-proof",
   1786       .method = MHD_HTTP_METHOD_GET,
   1787       .handler.get = &TEH_handler_kyc_proof,
   1788       .nargs = 1
   1789     },
   1790     {
   1791       .url = "kyc-start",
   1792       .method = MHD_HTTP_METHOD_POST,
   1793       .handler.post = &TEH_handler_kyc_start,
   1794       .nargs = 1
   1795     },
   1796     {
   1797       .url = "kyc-wallet",
   1798       .method = MHD_HTTP_METHOD_POST,
   1799       .handler.post = &TEH_handler_kyc_wallet,
   1800       .nargs = 0
   1801     },
   1802     {
   1803       .url = "kyc-webhook",
   1804       .method = MHD_HTTP_METHOD_GET,
   1805       .handler.get = &TEH_handler_kyc_webhook_get,
   1806       .nargs = 16, /* more is not plausible */
   1807       .nargs_is_upper_bound = true
   1808     },
   1809     {
   1810       .url = "kyc-info",
   1811       .method = MHD_HTTP_METHOD_GET,
   1812       .handler.get = &TEH_handler_kyc_info,
   1813       .nargs = 1
   1814     },
   1815     {
   1816       .url = "kyc-upload",
   1817       .method = MHD_HTTP_METHOD_POST,
   1818       .handler.post = &TEH_handler_kyc_upload,
   1819       .nargs = 1,
   1820       .nargs_is_upper_bound = true
   1821     },
   1822     {
   1823       .url = "kyc-webhook",
   1824       .method = MHD_HTTP_METHOD_POST,
   1825       .handler.post = &TEH_handler_kyc_webhook_post,
   1826       .nargs = 16, /* more is not plausible */
   1827       .nargs_is_upper_bound = true
   1828     },
   1829     /* POST management endpoints */
   1830     {
   1831       .url = "management",
   1832       .method = MHD_HTTP_METHOD_POST,
   1833       .handler.post = &handle_post_management,
   1834       .nargs = 4,
   1835       .nargs_is_upper_bound = true
   1836     },
   1837     /* GET management endpoints (we only really have "/management/keys") */
   1838     {
   1839       .url = "management",
   1840       .method = MHD_HTTP_METHOD_GET,
   1841       .handler.get = &handle_get_management,
   1842       .nargs = 1
   1843     },
   1844     /* auditor endpoints */
   1845     {
   1846       .url = "auditors",
   1847       .method = MHD_HTTP_METHOD_POST,
   1848       .handler.post = &handle_post_auditors,
   1849       .nargs = 4,
   1850       .nargs_is_upper_bound = true
   1851     },
   1852     /* AML endpoints */
   1853     {
   1854       .url = "aml",
   1855       .method = MHD_HTTP_METHOD_GET,
   1856       .handler.get = &handle_get_aml,
   1857       .nargs = 4,
   1858       .nargs_is_upper_bound = true
   1859     },
   1860     {
   1861       .url = "aml",
   1862       .method = MHD_HTTP_METHOD_POST,
   1863       .handler.post = &handle_post_aml,
   1864       .nargs = 2
   1865     },
   1866     {
   1867       .url = "aml-spa",
   1868       .method = MHD_HTTP_METHOD_GET,
   1869       .handler.get = &TEH_handler_aml_spa,
   1870       .nargs = 1,
   1871       .nargs_is_upper_bound = true
   1872     },
   1873     {
   1874       .url = "kyc-spa",
   1875       .method = MHD_HTTP_METHOD_GET,
   1876       .handler.get = &TEH_handler_kyc_spa,
   1877       .nargs = 1,
   1878       .nargs_is_upper_bound = true
   1879     },
   1880 
   1881     /* mark end of list */
   1882     {
   1883       .url = NULL
   1884     }
   1885   };
   1886   struct TEH_RequestContext *rc = *con_cls;
   1887   struct GNUNET_AsyncScopeSave old_scope;
   1888   const char *correlation_id = NULL;
   1889 
   1890   (void) cls;
   1891   (void) version;
   1892   if (NULL == rc)
   1893   {
   1894     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1895                 "Handling new request\n");
   1896     /* We're in a new async scope! */
   1897     rc = *con_cls = GNUNET_new (struct TEH_RequestContext);
   1898     rc->start_time = GNUNET_TIME_absolute_get ();
   1899     GNUNET_async_scope_fresh (&rc->async_scope_id);
   1900     TEH_check_invariants ();
   1901     rc->url = url;
   1902     rc->connection = connection;
   1903     /* We only read the correlation ID on the first callback for every client */
   1904     correlation_id = MHD_lookup_connection_value (connection,
   1905                                                   MHD_HEADER_KIND,
   1906                                                   "Taler-Correlation-Id");
   1907     if ( (NULL != correlation_id) &&
   1908          (GNUNET_YES !=
   1909           GNUNET_CURL_is_valid_scope_id (correlation_id)) )
   1910     {
   1911       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1912                   "illegal incoming correlation ID\n");
   1913       correlation_id = NULL;
   1914     }
   1915 
   1916     /* Check if upload is in bounds */
   1917     if ( (0 == strcasecmp (method,
   1918                            MHD_HTTP_METHOD_POST)) ||
   1919          (0 == strcasecmp (method,
   1920                            MHD_HTTP_METHOD_PATCH)) )
   1921     {
   1922       TALER_MHD_check_content_length (connection,
   1923                                       TALER_MHD_REQUEST_BUFFER_MAX);
   1924     }
   1925   }
   1926 
   1927   GNUNET_async_scope_enter (&rc->async_scope_id,
   1928                             &old_scope);
   1929   TEH_check_invariants ();
   1930   if (NULL != correlation_id)
   1931     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1932                 "Handling request (%s) for URL '%s', correlation_id=%s\n",
   1933                 method,
   1934                 url,
   1935                 correlation_id);
   1936   else
   1937     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1938                 "Handling request (%s) for URL '%s'\n",
   1939                 method,
   1940                 url);
   1941   /* on repeated requests, check our cache first */
   1942   if (NULL != rc->rh)
   1943   {
   1944     enum MHD_Result ret;
   1945     const char *start;
   1946 
   1947     if ('\0' == url[0])
   1948       /* strange, should start with '/', treat as just "/" */
   1949       url = "/";
   1950     start = strchr (url + 1, '/');
   1951     if (NULL == start)
   1952       start = "";
   1953     ret = proceed_with_handler (rc,
   1954                                 start,
   1955                                 upload_data,
   1956                                 upload_data_size);
   1957     if (GNUNET_OK !=
   1958         TALER_EXCHANGEDB_preflight (TEH_pg))
   1959     {
   1960       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1961                   "Handler %s left open database transaction behind!\n",
   1962                   url);
   1963       GNUNET_assert (0);
   1964     }
   1965     GNUNET_async_scope_restore (&old_scope);
   1966     return ret;
   1967   }
   1968 
   1969   if ( (0 == strcasecmp (method,
   1970                          MHD_HTTP_METHOD_OPTIONS)) &&
   1971        (0 == strcmp ("*",
   1972                      url)) )
   1973   {
   1974     enum MHD_Result ret;
   1975 
   1976     ret = TALER_MHD_reply_cors_preflight (connection);
   1977     GNUNET_async_scope_restore (&old_scope);
   1978     return ret;
   1979   }
   1980   if (0 == strcasecmp (method,
   1981                        MHD_HTTP_METHOD_HEAD))
   1982     method = MHD_HTTP_METHOD_GET; /* treat HEAD as GET here, MHD will do the rest */
   1983 
   1984   /* parse first part of URL */
   1985   {
   1986     bool found = false;
   1987     size_t tok_size;
   1988     const char *tok;
   1989     const char *rest;
   1990 
   1991     if ('\0' == url[0])
   1992       /* strange, should start with '/', treat as just "/" */
   1993       url = "/";
   1994     tok = url + 1;
   1995     rest = strchr (tok, '/');
   1996     if (NULL == rest)
   1997     {
   1998       tok_size = strlen (tok);
   1999     }
   2000     else
   2001     {
   2002       tok_size = rest - tok;
   2003       rest++; /* skip over '/' */
   2004     }
   2005     for (unsigned int i = 0; NULL != handlers[i].url; i++)
   2006     {
   2007       struct TEH_RequestHandler *rh = &handlers[i];
   2008 
   2009       if ( (0 != strncmp (tok,
   2010                           rh->url,
   2011                           tok_size)) ||
   2012            (tok_size != strlen (rh->url) ) )
   2013         continue;
   2014       found = true;
   2015       /* The URL is a match!  What we now do depends on the method. */
   2016       if (0 == strcasecmp (method,
   2017                            MHD_HTTP_METHOD_OPTIONS))
   2018       {
   2019         GNUNET_async_scope_restore (&old_scope);
   2020         return TALER_MHD_reply_cors_preflight (connection);
   2021       }
   2022       GNUNET_assert (NULL != rh->method);
   2023       if (0 == strcasecmp (method,
   2024                            rh->method))
   2025       {
   2026         enum MHD_Result ret;
   2027 
   2028         /* cache to avoid the loop next time */
   2029         rc->rh = rh;
   2030         /* run handler */
   2031         ret = proceed_with_handler (rc,
   2032                                     url + tok_size + 1,
   2033                                     upload_data,
   2034                                     upload_data_size);
   2035         if (GNUNET_OK !=
   2036             TALER_EXCHANGEDB_preflight (TEH_pg))
   2037         {
   2038           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2039                       "Handler %s left open database transaction behind!\n",
   2040                       url);
   2041           GNUNET_assert (0);
   2042         }
   2043         GNUNET_async_scope_restore (&old_scope);
   2044         return ret;
   2045       }
   2046     }
   2047 
   2048     if (found)
   2049     {
   2050       /* we found a matching address, but the method is wrong */
   2051       struct MHD_Response *reply;
   2052       enum MHD_Result ret;
   2053       char *allowed = NULL;
   2054 
   2055       GNUNET_break_op (0);
   2056       for (unsigned int i = 0; NULL != handlers[i].url; i++)
   2057       {
   2058         struct TEH_RequestHandler *rh = &handlers[i];
   2059 
   2060         if ( (0 != strncmp (tok,
   2061                             rh->url,
   2062                             tok_size)) ||
   2063              (tok_size != strlen (rh->url) ) )
   2064           continue;
   2065         if (NULL == allowed)
   2066         {
   2067           allowed = GNUNET_strdup (rh->method);
   2068         }
   2069         else
   2070         {
   2071           char *tmp;
   2072 
   2073           GNUNET_asprintf (&tmp,
   2074                            "%s, %s",
   2075                            allowed,
   2076                            rh->method);
   2077           GNUNET_free (allowed);
   2078           allowed = tmp;
   2079         }
   2080         if (0 == strcasecmp (rh->method,
   2081                              MHD_HTTP_METHOD_GET))
   2082         {
   2083           char *tmp;
   2084 
   2085           GNUNET_asprintf (&tmp,
   2086                            "%s, %s",
   2087                            allowed,
   2088                            MHD_HTTP_METHOD_HEAD);
   2089           GNUNET_free (allowed);
   2090           allowed = tmp;
   2091         }
   2092       }
   2093       reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
   2094                                     method);
   2095       GNUNET_break (MHD_YES ==
   2096                     MHD_add_response_header (reply,
   2097                                              MHD_HTTP_HEADER_ALLOW,
   2098                                              allowed));
   2099       GNUNET_free (allowed);
   2100       ret = MHD_queue_response (connection,
   2101                                 MHD_HTTP_METHOD_NOT_ALLOWED,
   2102                                 reply);
   2103       MHD_destroy_response (reply);
   2104       GNUNET_async_scope_restore (&old_scope);
   2105       return ret;
   2106     }
   2107   }
   2108 
   2109   /* No handler matches, generate not found */
   2110   {
   2111     enum MHD_Result ret;
   2112 
   2113     ret = TALER_MHD_reply_with_error (connection,
   2114                                       MHD_HTTP_NOT_FOUND,
   2115                                       TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
   2116                                       url);
   2117     GNUNET_async_scope_restore (&old_scope);
   2118     return ret;
   2119   }
   2120 }
   2121 
   2122 
   2123 /**
   2124  * Load configuration parameters for the exchange
   2125  * server into the corresponding global variables.
   2126  *
   2127  * @param cfg_fn name of our configuration file
   2128  * @return #GNUNET_OK on success
   2129  */
   2130 static enum GNUNET_GenericReturnValue
   2131 exchange_serve_process_config (const char *cfg_fn)
   2132 {
   2133   TEH_disable_direct_deposit
   2134     = GNUNET_CONFIGURATION_get_value_yesno (
   2135         TEH_cfg,
   2136         "exchange",
   2137         "DISABLE_DIRECT_DEPOSIT");
   2138   if (GNUNET_SYSERR == TEH_disable_direct_deposit)
   2139   {
   2140     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2141                                "exchange",
   2142                                "DISABLE_DIRECT_DEPOSIT",
   2143                                "Need YES or NO\n");
   2144     return GNUNET_SYSERR;
   2145   }
   2146   TEH_enable_kyc
   2147     = GNUNET_CONFIGURATION_get_value_yesno (
   2148         TEH_cfg,
   2149         "exchange",
   2150         "ENABLE_KYC");
   2151   if (GNUNET_SYSERR == TEH_enable_kyc)
   2152   {
   2153     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2154                                "exchange",
   2155                                "ENABLE_KYC",
   2156                                "Need YES or NO\n");
   2157     return GNUNET_SYSERR;
   2158   }
   2159   TEH_kyc_swap_tos_acceptance
   2160     = GNUNET_CONFIGURATION_get_value_yesno (
   2161         TEH_cfg,
   2162         "exchange",
   2163         "KYC_SWAP_TOS_ACCEPTANCE");
   2164   if (GNUNET_SYSERR == TEH_kyc_swap_tos_acceptance)
   2165     TEH_kyc_swap_tos_acceptance = GNUNET_NO;
   2166   if (GNUNET_OK !=
   2167       TALER_KYCLOGIC_kyc_init (TEH_cfg,
   2168                                cfg_fn))
   2169   {
   2170     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2171                 "Failed to load configuration `%s'. Exiting.\n",
   2172                 cfg_fn);
   2173     return GNUNET_SYSERR;
   2174   }
   2175   TEH_hard_limits
   2176     = TALER_KYCLOGIC_get_hard_limits ();
   2177   TEH_zero_limits
   2178     = TALER_KYCLOGIC_get_zero_limits ();
   2179   if (GNUNET_OK !=
   2180       GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
   2181                                              "exchange",
   2182                                              "MAX_REQUESTS",
   2183                                              &req_max))
   2184   {
   2185     req_max = ULLONG_MAX;
   2186   }
   2187   if (GNUNET_OK !=
   2188       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
   2189                                            "exchangedb",
   2190                                            "IDLE_RESERVE_EXPIRATION_TIME",
   2191                                            &TEH_reserve_closing_delay))
   2192   {
   2193     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2194                                "exchangedb",
   2195                                "IDLE_RESERVE_EXPIRATION_TIME");
   2196     /* use default */
   2197     TEH_reserve_closing_delay
   2198       = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
   2199                                        4);
   2200   }
   2201   if (GNUNET_OK !=
   2202       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
   2203                                            "exchange",
   2204                                            "DEFAULT_P2P_EXPIRATION",
   2205                                            &TEH_default_p2p_expiration))
   2206   {
   2207     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
   2208                                "exchange",
   2209                                "DEFAULT_P2P_EXPIRATION");
   2210     /* use default */
   2211     TEH_default_p2p_expiration
   2212       = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_WEEKS,
   2213                                        4);
   2214   }
   2215   if (GNUNET_OK !=
   2216       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
   2217                                            "exchangedb",
   2218                                            "MAX_AML_PROGRAM_RUNTIME",
   2219                                            &TEH_aml_program_timeout))
   2220   {
   2221     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2222                                "exchangedb",
   2223                                "MAX_AML_PROGRAM_RUNTIME");
   2224     /* use default */
   2225     TEH_aml_program_timeout
   2226       = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MINUTES,
   2227                                        1);
   2228   }
   2229 
   2230   if (GNUNET_OK !=
   2231       GNUNET_CONFIGURATION_get_value_time (TEH_cfg,
   2232                                            "exchange",
   2233                                            "MAX_KEYS_CACHING",
   2234                                            &TEH_max_keys_caching))
   2235   {
   2236     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2237                                "exchange",
   2238                                "MAX_KEYS_CACHING",
   2239                                "valid relative time expected");
   2240     return GNUNET_SYSERR;
   2241   }
   2242   if (GNUNET_OK !=
   2243       GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2244                                              "exchange",
   2245                                              "TOPLEVEL_REDIRECT_URL",
   2246                                              &toplevel_redirect_url))
   2247   {
   2248     toplevel_redirect_url = GNUNET_strdup ("/terms");
   2249   }
   2250   (void)
   2251   GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2252                                          "exchange",
   2253                                          "BANK_COMPLIANCE_LANGUAGE",
   2254                                          &TEH_bank_compliance_language);
   2255   (void)
   2256   GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2257                                          "exchange",
   2258                                          "AML_SPA_DIALECT",
   2259                                          &TEH_aml_spa_dialect);
   2260   if (GNUNET_OK !=
   2261       TALER_config_get_currency (TEH_cfg,
   2262                                  "exchange",
   2263                                  &TEH_currency))
   2264   {
   2265     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2266                                "exchange",
   2267                                "CURRENCY");
   2268     return GNUNET_SYSERR;
   2269   }
   2270 
   2271   if (GNUNET_OK !=
   2272       TALER_CONFIG_parse_currencies (TEH_cfg,
   2273                                      TEH_currency,
   2274                                      &num_cspecs,
   2275                                      &cspecs))
   2276     return GNUNET_SYSERR;
   2277   for (unsigned int i = 0; i<num_cspecs; i++)
   2278   {
   2279     struct TALER_CurrencySpecification *cspec;
   2280 
   2281     cspec = &cspecs[i];
   2282     if (0 == strcasecmp (TEH_currency,
   2283                          cspec->currency))
   2284     {
   2285       TEH_cspec = cspec;
   2286       break;
   2287     }
   2288   }
   2289   /* currency parser must provide default spec for main currency */
   2290   GNUNET_assert (NULL != TEH_cspec);
   2291   GNUNET_assert (GNUNET_OK ==
   2292                  TALER_amount_set_zero (TEH_currency,
   2293                                         &TEH_stefan_abs));
   2294   if (GNUNET_SYSERR ==
   2295       TALER_config_get_amount (TEH_cfg,
   2296                                "exchange",
   2297                                "STEFAN_ABS",
   2298                                &TEH_stefan_abs))
   2299   {
   2300     GNUNET_break (0);
   2301     return GNUNET_SYSERR;
   2302   }
   2303   GNUNET_assert (GNUNET_OK ==
   2304                  TALER_amount_set_zero (TEH_currency,
   2305                                         &TEH_stefan_log));
   2306   if (GNUNET_SYSERR ==
   2307       TALER_config_get_amount (TEH_cfg,
   2308                                "exchange",
   2309                                "STEFAN_LOG",
   2310                                &TEH_stefan_log))
   2311   {
   2312     GNUNET_break (0);
   2313     return GNUNET_SYSERR;
   2314   }
   2315   if ( (GNUNET_OK !=
   2316         TALER_config_get_amount (TEH_cfg,
   2317                                  "exchange",
   2318                                  "TINY_AMOUNT",
   2319                                  &TEH_tiny_amount)) &&
   2320        (GNUNET_OK !=
   2321         TALER_config_get_amount (TEH_cfg,
   2322                                  "auditor",
   2323                                  "TINY_AMOUNT",
   2324                                  &TEH_tiny_amount)) )
   2325   {
   2326     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2327                                "exchange",
   2328                                "TINY_AMOUNT");
   2329     return GNUNET_SYSERR;
   2330   }
   2331   TEH_stefan_lin = 0.0f;
   2332   if (GNUNET_SYSERR ==
   2333       GNUNET_CONFIGURATION_get_value_float (TEH_cfg,
   2334                                             "exchange",
   2335                                             "STEFAN_LIN",
   2336                                             &TEH_stefan_lin))
   2337   {
   2338     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2339                                "exchange",
   2340                                "STEFAN_LIN",
   2341                                "must be a floating point");
   2342     return GNUNET_SYSERR;
   2343   }
   2344 
   2345   if (GNUNET_OK !=
   2346       GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2347                                              "exchange",
   2348                                              "BASE_URL",
   2349                                              &TEH_base_url))
   2350   {
   2351     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2352                                "exchange",
   2353                                "BASE_URL");
   2354     return GNUNET_SYSERR;
   2355   }
   2356   if ( (! TALER_url_valid_charset (TEH_base_url)) ||
   2357        (! TALER_is_web_url (TEH_base_url) ) )
   2358   {
   2359     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2360                                "exchange",
   2361                                "BASE_URL",
   2362                                "invalid URL");
   2363     return GNUNET_SYSERR;
   2364   }
   2365   if (GNUNET_OK !=
   2366       GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2367                                              "exchange",
   2368                                              "SHOPPING_URL",
   2369                                              &TEH_shopping_url))
   2370   {
   2371     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
   2372                                "exchange",
   2373                                "SHOPPING_URL");
   2374   }
   2375   if (GNUNET_OK !=
   2376       GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2377                                              "exchange",
   2378                                              "TERMS_ETAG",
   2379                                              &TEH_tos_etag))
   2380   {
   2381     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
   2382                                "exchange",
   2383                                "TERMS_ETAG");
   2384   }
   2385   if ( (NULL != TEH_shopping_url) &&
   2386        (! TALER_is_web_url (TEH_shopping_url)) )
   2387   {
   2388     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2389                                "exchange",
   2390                                "SHOPPING_URL",
   2391                                "invalid URL");
   2392     return GNUNET_SYSERR;
   2393   }
   2394   if (GNUNET_OK ==
   2395       GNUNET_CONFIGURATION_get_value_filename (TEH_cfg,
   2396                                                "exchange",
   2397                                                "AML_SPA_DIR",
   2398                                                &TEH_aml_spa_dir))
   2399   {
   2400     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2401                 "Loading exchange AML SPA from %s\n",
   2402                 TEH_aml_spa_dir);
   2403   }
   2404   else
   2405   {
   2406     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2407                 "Loading exchange AML SPA from default location\n");
   2408   }
   2409   if (GNUNET_OK ==
   2410       GNUNET_CONFIGURATION_get_value_filename (TEH_cfg,
   2411                                                "exchange",
   2412                                                "KYC_SPA_DIR",
   2413                                                &TEH_kyc_spa_dir))
   2414   {
   2415     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2416                 "Loading exchange KYC SPA from %s\n",
   2417                 TEH_kyc_spa_dir);
   2418   }
   2419   else
   2420   {
   2421     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2422                 "Loading exchange KYC SPA from default location\n");
   2423   }
   2424 
   2425   {
   2426     char *master_public_key_str;
   2427 
   2428     if (GNUNET_OK !=
   2429         GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2430                                                "exchange",
   2431                                                "MASTER_PUBLIC_KEY",
   2432                                                &master_public_key_str))
   2433     {
   2434       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2435                                  "exchange",
   2436                                  "MASTER_PUBLIC_KEY");
   2437       return GNUNET_SYSERR;
   2438     }
   2439     if (GNUNET_OK !=
   2440         GNUNET_CRYPTO_eddsa_public_key_from_string (
   2441           master_public_key_str,
   2442           strlen (master_public_key_str),
   2443           &TEH_master_public_key.eddsa_pub))
   2444     {
   2445       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2446                                  "exchange",
   2447                                  "MASTER_PUBLIC_KEY",
   2448                                  "invalid base32 encoding for a master public key");
   2449       GNUNET_free (master_public_key_str);
   2450       return GNUNET_SYSERR;
   2451     }
   2452     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2453                 "Launching exchange with public key `%s'...\n",
   2454                 master_public_key_str);
   2455     GNUNET_free (master_public_key_str);
   2456   }
   2457 
   2458   {
   2459     char *attr_enc_key_str;
   2460 
   2461     if (GNUNET_OK !=
   2462         GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2463                                                "exchange",
   2464                                                "ATTRIBUTE_ENCRYPTION_KEY",
   2465                                                &attr_enc_key_str))
   2466     {
   2467       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2468                                  "exchange",
   2469                                  "ATTRIBUTE_ENCRYPTION_KEY");
   2470       return GNUNET_SYSERR;
   2471     }
   2472     GNUNET_CRYPTO_hash (attr_enc_key_str,
   2473                         strlen (attr_enc_key_str),
   2474                         &TEH_attribute_key.hash);
   2475     GNUNET_free (attr_enc_key_str);
   2476   }
   2477 
   2478   {
   2479     char *pdf_data;
   2480 
   2481     if (GNUNET_OK ==
   2482         GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2483                                                "exchange",
   2484                                                "GLOBAL_PDF_FORM_DATA",
   2485                                                &pdf_data))
   2486     {
   2487       json_error_t err;
   2488 
   2489       TEH_global_pdf_form_data = json_loads (pdf_data,
   2490                                              JSON_REJECT_DUPLICATES,
   2491                                              &err);
   2492       GNUNET_free (pdf_data);
   2493       if (NULL == TEH_global_pdf_form_data)
   2494       {
   2495         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2496                                    "exchange",
   2497                                    "GLOBAL_PDF_FORM_DATA",
   2498                                    err.text);
   2499         return GNUNET_SYSERR;
   2500       }
   2501     }
   2502   }
   2503 
   2504   for (unsigned int i = 0; i<MAX_DB_RETRIES; i++)
   2505   {
   2506     TEH_pg = TALER_EXCHANGEDB_connect (TEH_cfg);
   2507     if (NULL != TEH_pg)
   2508       break;
   2509     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2510                 "Failed to connect to DB, will try again %u times\n",
   2511                 MAX_DB_RETRIES - i);
   2512     sleep (1);
   2513   }
   2514   if (NULL == TEH_pg)
   2515   {
   2516     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2517                 "Failed to initialize DB subsystem. Giving up.\n");
   2518     return GNUNET_SYSERR;
   2519   }
   2520   return GNUNET_OK;
   2521 }
   2522 
   2523 
   2524 /**
   2525  * Called when the main thread exits, writes out performance
   2526  * stats if requested.
   2527  */
   2528 static void
   2529 write_stats (void)
   2530 {
   2531   struct GNUNET_DISK_FileHandle *fh;
   2532   pid_t pid = getpid ();
   2533   char *benchmark_dir;
   2534   char *s;
   2535   struct rusage usage;
   2536 
   2537   benchmark_dir = getenv ("GNUNET_BENCHMARK_DIR");
   2538   if (NULL == benchmark_dir)
   2539     return;
   2540   GNUNET_asprintf (&s,
   2541                    "%s/taler-exchange-%llu.txt",
   2542                    benchmark_dir,
   2543                    (unsigned long long) pid);
   2544   fh = GNUNET_DISK_file_open (s,
   2545                               (GNUNET_DISK_OPEN_WRITE
   2546                                | GNUNET_DISK_OPEN_TRUNCATE
   2547                                | GNUNET_DISK_OPEN_CREATE),
   2548                               (GNUNET_DISK_PERM_USER_READ
   2549                                | GNUNET_DISK_PERM_USER_WRITE));
   2550   GNUNET_free (s);
   2551   if (NULL == fh)
   2552     return; /* permission denied? */
   2553 
   2554   /* Collect stats, summed up for all threads */
   2555   GNUNET_assert (0 ==
   2556                  getrusage (RUSAGE_SELF,
   2557                             &usage));
   2558   GNUNET_asprintf (&s,
   2559                    "time_exchange sys %llu user %llu\n",
   2560                    (unsigned long long) (usage.ru_stime.tv_sec * 1000 * 1000
   2561                                          + usage.ru_stime.tv_usec),
   2562                    (unsigned long long) (usage.ru_utime.tv_sec * 1000 * 1000
   2563                                          + usage.ru_utime.tv_usec));
   2564   GNUNET_assert (GNUNET_SYSERR !=
   2565                  GNUNET_DISK_file_write_blocking (fh,
   2566                                                   s,
   2567                                                   strlen (s)));
   2568   GNUNET_free (s);
   2569   GNUNET_assert (GNUNET_OK ==
   2570                  GNUNET_DISK_file_close (fh));
   2571 }
   2572 
   2573 
   2574 /* Developer logic for supporting the `-f' option. */
   2575 #if HAVE_DEVELOPER
   2576 
   2577 /**
   2578  * Option `-f' (specifies an input file to give to the HTTP server).
   2579  */
   2580 static char *input_filename;
   2581 
   2582 
   2583 /**
   2584  * Run 'nc' or 'ncat' as a fake HTTP client using #input_filename
   2585  * as the input for the request.  If launching the client worked,
   2586  * run the #TEH_KS_loop() event loop as usual.
   2587  *
   2588  * @return child pid
   2589  */
   2590 static pid_t
   2591 run_fake_client (void)
   2592 {
   2593   unsigned long long serve_port;
   2594   pid_t cld;
   2595   char ports[6];
   2596   int fd;
   2597   bool use_stdin;
   2598 
   2599   if (GNUNET_OK !=
   2600       GNUNET_CONFIGURATION_get_value_number (TEH_cfg,
   2601                                              "exchange",
   2602                                              "PORT",
   2603                                              &serve_port))
   2604   {
   2605     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   2606                                "exchange",
   2607                                "PORT",
   2608                                "port number required (for nc)");
   2609     return -1;
   2610   }
   2611   /* Duping to STDIN and fork() mess up gcc's analysis
   2612      badly, disable diagnostics. */
   2613 #pragma GCC diagnostic ignored "-Wanalyzer-fd-leak"
   2614 #pragma GCC diagnostic ignored "-Wanalyzer-fd-use-without-check"
   2615 #pragma GCC diagnostic ignored "-Wanalyzer-fd-double-close"
   2616 #pragma GCC diagnostic ignored "-Wanalyzer-fd-use-after-close"
   2617   use_stdin = (0 == strcmp (input_filename,
   2618                             "-"));
   2619   if (use_stdin)
   2620     fd = STDIN_FILENO;
   2621   else
   2622     fd = open (input_filename,
   2623                O_RDONLY);
   2624   if (-1 == fd)
   2625   {
   2626     fprintf (stderr,
   2627              "Failed to open `%s': %s\n",
   2628              input_filename,
   2629              strerror (errno));
   2630     return -1;
   2631   }
   2632   /* Fake HTTP client request with #input_filename as input.
   2633      We do this using the nc tool. */
   2634   GNUNET_snprintf (ports,
   2635                    sizeof (ports),
   2636                    "%u",
   2637                    (unsigned int) serve_port);
   2638   if (0 == (cld = fork ()))
   2639   {
   2640     if (! use_stdin)
   2641     {
   2642       GNUNET_break (0 == close (STDIN_FILENO));
   2643       GNUNET_break (STDIN_FILENO ==
   2644                     dup2 (fd,
   2645                           STDIN_FILENO));
   2646       GNUNET_break (0 == close (fd));
   2647     }
   2648     if ( (0 != execlp ("nc",
   2649                        "nc",
   2650                        "localhost",
   2651                        ports,
   2652                        "-w", "30",
   2653                        NULL)) &&
   2654          (0 != execlp ("ncat",
   2655                        "ncat",
   2656                        "localhost",
   2657                        ports,
   2658                        "-i", "30",
   2659                        NULL)) )
   2660     {
   2661       fprintf (stderr,
   2662                "Failed to run both `nc' and `ncat': %s\n",
   2663                strerror (errno));
   2664     }
   2665     _exit (1);
   2666   }
   2667   /* parent process */
   2668   if (! use_stdin)
   2669     GNUNET_break (0 == close (fd));
   2670   return cld;
   2671 #pragma GCC diagnostic pop
   2672 #pragma GCC diagnostic pop
   2673 #pragma GCC diagnostic pop
   2674 #pragma GCC diagnostic pop
   2675 }
   2676 
   2677 
   2678 /**
   2679  * Run the exchange to serve a single request only, without threads.
   2680  *
   2681  * @return #GNUNET_OK on success
   2682  */
   2683 static void
   2684 run_single_request (void)
   2685 {
   2686   pid_t xfork;
   2687 
   2688   xfork = fork ();
   2689   if (-1 == xfork)
   2690   {
   2691     GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
   2692                          "fork");
   2693     global_ret = EXIT_NO_RESTART;
   2694     GNUNET_SCHEDULER_shutdown ();
   2695     return;
   2696   }
   2697   if (0 == xfork)
   2698   {
   2699     pid_t cld;
   2700 
   2701     cld = run_fake_client ();
   2702     if (-1 == cld)
   2703       _exit (EXIT_FAILURE);
   2704     _exit (EXIT_SUCCESS);
   2705   }
   2706 
   2707   {
   2708     int status;
   2709 
   2710     if (xfork != waitpid (xfork,
   2711                           &status,
   2712                           0))
   2713       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2714                   "Waiting for `nc' child failed: %s\n",
   2715                   strerror (errno));
   2716   }
   2717 }
   2718 
   2719 
   2720 /* end of HAVE_DEVELOPER */
   2721 #endif
   2722 
   2723 
   2724 /**
   2725  * Signature of the callback used by MHD to notify the application
   2726  * about completed connections.  If we are running in test-mode with
   2727  * an input_filename, this function is used to terminate the HTTPD
   2728  * after the first request has been processed.
   2729  *
   2730  * @param cls client-defined closure, NULL
   2731  * @param connection connection handle (ignored)
   2732  * @param socket_context socket-specific pointer (ignored)
   2733  * @param toe reason for connection notification
   2734  */
   2735 static void
   2736 connection_done (void *cls,
   2737                  struct MHD_Connection *connection,
   2738                  void **socket_context,
   2739                  enum MHD_ConnectionNotificationCode toe)
   2740 {
   2741   (void) cls;
   2742   (void) connection;
   2743   (void) socket_context;
   2744 
   2745   switch (toe)
   2746   {
   2747   case MHD_CONNECTION_NOTIFY_STARTED:
   2748     active_connections++;
   2749     break;
   2750   case MHD_CONNECTION_NOTIFY_CLOSED:
   2751     active_connections--;
   2752     if (TEH_suicide &&
   2753         (0 == active_connections) )
   2754       GNUNET_SCHEDULER_shutdown ();
   2755     break;
   2756   }
   2757 #if HAVE_DEVELOPER
   2758   /* We only act if the connection is closed. */
   2759   if (MHD_CONNECTION_NOTIFY_CLOSED != toe)
   2760     return;
   2761   if (NULL != input_filename)
   2762     GNUNET_SCHEDULER_shutdown ();
   2763 #endif
   2764 }
   2765 
   2766 
   2767 /**
   2768  * Function run on shutdown.
   2769  *
   2770  * @param cls NULL
   2771  */
   2772 static void
   2773 do_shutdown (void *cls)
   2774 {
   2775   (void) cls;
   2776   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2777               "Shutdown of taler-exchange-httpd\n");
   2778   TALER_MHD_daemons_halt ();
   2779   TEH_resume_keys_requests (true);
   2780   TEH_batch_deposit_cleanup ();
   2781   TEH_withdraw_cleanup ();
   2782   TEH_reserves_close_cleanup ();
   2783   TEH_reserves_purse_cleanup ();
   2784   TEH_purses_merge_cleanup ();
   2785   TEH_kyc_wallet_cleanup ();
   2786   TEH_kyc_upload_cleanup ();
   2787   TEH_deposits_get_cleanup ();
   2788   TEH_reserves_get_cleanup ();
   2789   TEH_purses_get_cleanup ();
   2790   TEH_kyc_check_cleanup ();
   2791   TEH_kyc_info_cleanup ();
   2792   TEH_kyc_proof_cleanup ();
   2793   TEH_kyc_start_cleanup ();
   2794   TEH_aml_decision_cleanup ();
   2795   TEH_handler_aml_attributes_get_cleanup ();
   2796   TALER_KYCLOGIC_kyc_done ();
   2797   TALER_MHD_daemons_destroy ();
   2798   TEH_keys_finished ();
   2799   if (NULL != TEH_pg)
   2800   {
   2801     TALER_EXCHANGEDB_disconnect (TEH_pg);
   2802     TEH_pg = NULL;
   2803   }
   2804   if (NULL != TEH_curl_ctx)
   2805   {
   2806     GNUNET_CURL_fini (TEH_curl_ctx);
   2807     TEH_curl_ctx = NULL;
   2808   }
   2809   if (NULL != exchange_curl_rc)
   2810   {
   2811     GNUNET_CURL_gnunet_rc_destroy (exchange_curl_rc);
   2812     exchange_curl_rc = NULL;
   2813   }
   2814   TALER_TEMPLATING_done ();
   2815   TEH_cspec = NULL;
   2816   TALER_CONFIG_free_currencies (num_cspecs,
   2817                                 cspecs);
   2818   num_cspecs = 0;
   2819   cspecs = NULL;
   2820 }
   2821 
   2822 
   2823 /**
   2824  * Callback invoked on every listen socket to start the
   2825  * respective MHD HTTP daemon.
   2826  *
   2827  * @param cls unused
   2828  * @param lsock the listen socket
   2829  */
   2830 static void
   2831 start_daemon (void *cls,
   2832               int lsock)
   2833 {
   2834   struct MHD_Daemon *mhd;
   2835 
   2836   (void) cls;
   2837   GNUNET_assert (-1 != lsock);
   2838   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME
   2839                           | MHD_USE_PIPE_FOR_SHUTDOWN
   2840                           | MHD_USE_DEBUG | MHD_USE_DUAL_STACK
   2841                           | MHD_USE_TCP_FASTOPEN,
   2842                           0, /* already bound */
   2843                           NULL, NULL,
   2844                           &handle_mhd_request, NULL,
   2845                           MHD_OPTION_LISTEN_SOCKET,
   2846                           lsock,
   2847                           MHD_OPTION_EXTERNAL_LOGGER,
   2848                           &TALER_MHD_handle_logs,
   2849                           NULL,
   2850                           MHD_OPTION_NOTIFY_COMPLETED,
   2851                           &handle_mhd_completion_callback,
   2852                           NULL,
   2853                           MHD_OPTION_NOTIFY_CONNECTION,
   2854                           &connection_done,
   2855                           NULL,
   2856                           MHD_OPTION_CONNECTION_TIMEOUT,
   2857                           connection_timeout,
   2858                           (0 == allow_address_reuse)
   2859                           ? MHD_OPTION_END
   2860                           : MHD_OPTION_LISTENING_ADDRESS_REUSE,
   2861                           (unsigned int) allow_address_reuse,
   2862                           MHD_OPTION_END);
   2863   if (NULL == mhd)
   2864   {
   2865     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2866                 "Failed to launch HTTP service!\n");
   2867     GNUNET_SCHEDULER_shutdown ();
   2868     return;
   2869   }
   2870   have_daemons = true;
   2871   TALER_MHD_daemon_start (mhd);
   2872 }
   2873 
   2874 
   2875 /**
   2876  * Main function that will be run by the scheduler.
   2877  *
   2878  * @param cls closure
   2879  * @param args remaining command-line arguments
   2880  * @param cfgfile name of the configuration file used (for saving, can be
   2881  *        NULL!)
   2882  * @param config configuration
   2883  */
   2884 static void
   2885 run (void *cls,
   2886      char *const *args,
   2887      const char *cfgfile,
   2888      const struct GNUNET_CONFIGURATION_Handle *config)
   2889 {
   2890   enum TALER_MHD_GlobalOptions go;
   2891   enum GNUNET_GenericReturnValue ret;
   2892 
   2893   (void) cls;
   2894   (void) args;
   2895   (void ) cfgfile;
   2896   go = TALER_MHD_GO_NONE;
   2897   if (connection_close)
   2898     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
   2899   TALER_MHD_setup (go);
   2900   TEH_cfg = config;
   2901 
   2902   if (GNUNET_OK !=
   2903       exchange_serve_process_config (cfgfile))
   2904   {
   2905     global_ret = EXIT_NOTCONFIGURED;
   2906     GNUNET_SCHEDULER_shutdown ();
   2907     return;
   2908   }
   2909   if (GNUNET_OK !=
   2910       TEH_spa_init ())
   2911   {
   2912     global_ret = EXIT_NOTCONFIGURED;
   2913     GNUNET_SCHEDULER_shutdown ();
   2914     return;
   2915   }
   2916   if (GNUNET_OK !=
   2917       TALER_TEMPLATING_init (TALER_EXCHANGE_project_data ()))
   2918   {
   2919     global_ret = EXIT_NOTINSTALLED;
   2920     GNUNET_SCHEDULER_shutdown ();
   2921     return;
   2922   }
   2923   if (GNUNET_SYSERR ==
   2924       TALER_EXCHANGEDB_preflight (TEH_pg))
   2925   {
   2926     GNUNET_break (0);
   2927     global_ret = EXIT_NO_RESTART;
   2928     GNUNET_SCHEDULER_shutdown ();
   2929     return;
   2930   }
   2931   /* Initialize age restriction from [exchange] section */
   2932   TEH_age_restriction_enabled = false;
   2933   memset (&TEH_age_restriction_mask,
   2934           0,
   2935           sizeof (TEH_age_restriction_mask));
   2936   if (GNUNET_YES ==
   2937       GNUNET_CONFIGURATION_get_value_yesno (TEH_cfg,
   2938                                             "exchange",
   2939                                             "AGE_RESTRICTION_ENABLED"))
   2940   {
   2941     char *groups = NULL;
   2942     struct TALER_AgeMask mask = {0};
   2943 
   2944     if (GNUNET_OK !=
   2945         GNUNET_CONFIGURATION_get_value_string (TEH_cfg,
   2946                                                "exchange",
   2947                                                "AGE_GROUPS",
   2948                                                &groups))
   2949       groups = GNUNET_strdup (
   2950         "8:10:12:14:16:18:21");
   2951     if (GNUNET_OK !=
   2952         TALER_parse_age_group_string (groups,
   2953                                       &mask))
   2954     {
   2955       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2956                   "Invalid AGE_GROUPS value in [exchange]: %s\n",
   2957                   groups);
   2958       GNUNET_free (groups);
   2959       global_ret = EXIT_NOTINSTALLED;
   2960       GNUNET_SCHEDULER_shutdown ();
   2961       return;
   2962     }
   2963     GNUNET_free (groups);
   2964     TEH_age_restriction_mask = mask;
   2965     TEH_age_restriction_enabled = true;
   2966   }
   2967   if (GNUNET_OK !=
   2968       TEH_keys_init ())
   2969   {
   2970     GNUNET_break (0);
   2971     global_ret = EXIT_NO_RESTART;
   2972     GNUNET_SCHEDULER_shutdown ();
   2973     return;
   2974   }
   2975 
   2976   TEH_load_terms (TEH_cfg);
   2977   TEH_curl_ctx
   2978     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   2979                         &exchange_curl_rc);
   2980   if (NULL == TEH_curl_ctx)
   2981   {
   2982     GNUNET_break (0);
   2983     global_ret = EXIT_NO_RESTART;
   2984     GNUNET_SCHEDULER_shutdown ();
   2985     return;
   2986   }
   2987   exchange_curl_rc = GNUNET_CURL_gnunet_rc_create (TEH_curl_ctx);
   2988   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   2989                                  NULL);
   2990   ret = TALER_MHD_listen_bind (TEH_cfg,
   2991                                "exchange",
   2992                                &start_daemon,
   2993                                NULL);
   2994   switch (ret)
   2995   {
   2996   case GNUNET_SYSERR:
   2997     global_ret = EXIT_NOTCONFIGURED;
   2998     GNUNET_SCHEDULER_shutdown ();
   2999     return;
   3000   case GNUNET_NO:
   3001     if (! have_daemons)
   3002     {
   3003       global_ret = EXIT_NOTCONFIGURED;
   3004       GNUNET_SCHEDULER_shutdown ();
   3005       return;
   3006     }
   3007     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3008                 "Could not open all configured listen sockets\n");
   3009     break;
   3010   case GNUNET_OK:
   3011     break;
   3012   }
   3013   global_ret = EXIT_SUCCESS;
   3014   atexit (&write_stats);
   3015 #if HAVE_DEVELOPER
   3016   if (NULL != input_filename)
   3017     run_single_request ();
   3018 #endif
   3019 }
   3020 
   3021 
   3022 /**
   3023  * The main function of the taler-exchange-httpd server ("the exchange").
   3024  *
   3025  * @param argc number of arguments from the command line
   3026  * @param argv command line arguments
   3027  * @return 0 ok, 1 on error
   3028  */
   3029 int
   3030 main (int argc,
   3031       char *const *argv)
   3032 {
   3033   const struct GNUNET_GETOPT_CommandLineOption options[] = {
   3034     GNUNET_GETOPT_option_flag ('a',
   3035                                "allow-timetravel",
   3036                                "allow clients to request /keys for arbitrary timestamps (for testing and development only)",
   3037                                &TEH_allow_keys_timetravel),
   3038     GNUNET_GETOPT_option_flag ('C',
   3039                                "connection-close",
   3040                                "force HTTP connections to be closed after each request",
   3041                                &connection_close),
   3042     GNUNET_GETOPT_option_flag ('I',
   3043                                "check-invariants",
   3044                                "enable expensive invariant checks",
   3045                                &TEH_check_invariants_flag),
   3046     GNUNET_GETOPT_option_flag ('r',
   3047                                "allow-reuse-address",
   3048                                "allow multiple HTTPDs to listen to the same port",
   3049                                &allow_address_reuse),
   3050     GNUNET_GETOPT_option_uint ('t',
   3051                                "timeout",
   3052                                "SECONDS",
   3053                                "after how long do connections timeout by default (in seconds)",
   3054                                &connection_timeout),
   3055     GNUNET_GETOPT_option_timetravel ('T',
   3056                                      "timetravel"),
   3057 #if HAVE_DEVELOPER
   3058     GNUNET_GETOPT_option_filename ('f',
   3059                                    "file-input",
   3060                                    "FILENAME",
   3061                                    "run in test-mode using FILENAME as the HTTP request to process, use '-' to read from stdin",
   3062                                    &input_filename),
   3063 #endif
   3064     GNUNET_GETOPT_option_help (
   3065       TALER_EXCHANGE_project_data (),
   3066       "HTTP server providing a RESTful API to access a Taler exchange"),
   3067     GNUNET_GETOPT_OPTION_END
   3068   };
   3069   enum GNUNET_GenericReturnValue ret;
   3070 
   3071   ret = GNUNET_PROGRAM_run (
   3072     TALER_EXCHANGE_project_data (),
   3073     argc, argv,
   3074     "taler-exchange-httpd",
   3075     "Taler exchange HTTP service",
   3076     options,
   3077     &run, NULL);
   3078   if (GNUNET_SYSERR == ret)
   3079     return EXIT_INVALIDARGUMENT;
   3080   if (GNUNET_NO == ret)
   3081     return EXIT_SUCCESS;
   3082   return global_ret;
   3083 }
   3084 
   3085 
   3086 /* end of taler-exchange-httpd.c */