LCOV - code coverage report
Current view: top level - exchange - taler-exchange-httpd.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 442 732 60.4 %
Date: 2025-07-14 11:22:44 Functions: 23 26 88.5 %

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

Generated by: LCOV version 1.16