merchant

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

taler-merchant-httpd.c (55361B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014-2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file src/backend/taler-merchant-httpd.c
     18  * @brief HTTP serving layer intended to perform crypto-work and
     19  * communication with the exchange
     20  * @author Marcello Stanisci
     21  * @author Christian Grothoff
     22  * @author Florian Dold
     23  * @author Priscilla HUANG
     24  */
     25 #include "platform.h"
     26 #include <taler/taler_dbevents.h>
     27 #include <taler/taler_bank_service.h>
     28 #include <taler/taler_mhd_lib.h>
     29 #include <taler/taler_templating_lib.h>
     30 #include <taler/taler_exchange_service.h>
     31 #include <donau/donau_service.h>
     32 #include "taler/taler_merchant_util.h"
     33 #include "taler-merchant-httpd_auth.h"
     34 #include "taler-merchant-httpd_dispatcher.h"
     35 #include "taler-merchant-httpd_exchanges.h"
     36 #include "taler-merchant-httpd_helper.h"
     37 #include "taler-merchant-httpd_mhd.h"
     38 #include "taler-merchant-httpd_mfa.h"
     39 #include "taler-merchant-httpd_post-private-orders.h"
     40 #include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h"
     41 #include "taler-merchant-httpd_post-challenge-ID.h"
     42 #include "taler-merchant-httpd_get-orders-ORDER_ID.h"
     43 #include "taler-merchant-httpd_get-sessions-SESSION_ID.h"
     44 #include "taler-merchant-httpd_get-exchanges.h"
     45 #include "taler-merchant-httpd_get-webui.h"
     46 #include "taler-merchant-httpd_get-terms.h"
     47 #include "taler-merchant-httpd_get-private-kyc.h"
     48 #include "taler-merchant-httpd_get-private-statistics-report-transactions.h"
     49 #include "taler-merchant-httpd_post-private-donau.h"
     50 #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h"
     51 #include "taler-merchant-httpd_get-private-orders.h"
     52 #include "taler-merchant-httpd_post-orders-ORDER_ID-pay.h"
     53 #include "taler-merchant-httpd_post-orders-ORDER_ID-refund.h"
     54 #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
     55 #include "merchant-database/iterate_instances.h"
     56 #include "merchant-database/set_instance.h"
     57 #include "merchant-database/iterate_accounts_by_instance.h"
     58 #include "merchant-database/event_listen.h"
     59 #include "merchant-database/preflight.h"
     60 #include "merchant-database/event_notify.h"
     61 
     62 /**
     63  * Backlog for listen operation on unix-domain sockets.
     64  */
     65 #define UNIX_BACKLOG 500
     66 
     67 /**
     68  * Default maximum upload size permitted.  Can be overridden
     69  * per handler.
     70  */
     71 #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024)
     72 
     73 char *TMH_currency;
     74 
     75 char *TMH_base_url;
     76 
     77 char *TMH_spa_dir;
     78 
     79 char *TMH_helper_email;
     80 
     81 char *TMH_helper_sms;
     82 
     83 char *TMH_phone_regex;
     84 
     85 regex_t TMH_phone_rx;
     86 
     87 char *TMH_allowed_payment_targets;
     88 
     89 char *TMH_default_persona;
     90 
     91 char *TMH_payment_target_regex;
     92 
     93 regex_t TMH_payment_target_re;
     94 
     95 int TMH_force_audit;
     96 
     97 struct TALER_MERCHANTDB_PostgresContext *TMH_db;
     98 
     99 struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map;
    100 
    101 struct GNUNET_TIME_Relative TMH_default_pay_delay;
    102 
    103 struct GNUNET_TIME_Relative TMH_default_refund_delay;
    104 
    105 struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay;
    106 
    107 enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval;
    108 
    109 int TMH_strict_v19;
    110 
    111 int TMH_auth_disabled;
    112 
    113 int TMH_have_self_provisioning;
    114 
    115 enum TEH_TanChannelSet TEH_mandatory_tan_channels;
    116 
    117 struct GNUNET_TIME_Relative TMH_legal_expiration;
    118 
    119 unsigned int TMH_num_cspecs;
    120 
    121 json_t *TMH_global_spa_config_data;
    122 
    123 struct TALER_CurrencySpecification *TMH_cspecs;
    124 
    125 struct GNUNET_CURL_Context *TMH_curl_ctx;
    126 
    127 /**
    128  * Event handler for instance settings changes.
    129  */
    130 static struct GNUNET_DB_EventHandler *instance_eh;
    131 
    132 /**
    133  * True if we started any HTTP daemon.
    134  */
    135 static bool have_daemons;
    136 
    137 /**
    138  * Should a "Connection: close" header be added to each HTTP response?
    139  */
    140 static int merchant_connection_close;
    141 
    142 /**
    143  * Should we enable HTTP/2 and HTTP/3 when talking to the exchange
    144  * (and donau)?  Those are not expected to be terribly beneficial for
    145  * a server with stable connections to an exchange, but they could
    146  * cause stability issues with libcurl.  Per default, we *enforce*
    147  * HTTP/1.x-only, as that is the conservative and most tested code
    148  * path.
    149  */
    150 static int enable_h3;
    151 
    152 /**
    153  * Context for integrating #TMH_curl_ctx with the
    154  * GNUnet event loop.
    155  */
    156 static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
    157 
    158 /**
    159  * Global return code
    160  */
    161 static int global_ret;
    162 
    163 /**
    164  * Our configuration.
    165  */
    166 const struct GNUNET_CONFIGURATION_Handle *TMH_cfg;
    167 
    168 
    169 void
    170 TMH_wire_method_free (struct TMH_WireMethod *wm)
    171 {
    172   GNUNET_free (wm->payto_uri.full_payto);
    173   GNUNET_free (wm->wire_method);
    174   GNUNET_free (wm->extra_wire_subject_metadata);
    175   GNUNET_free (wm->credit_facade_url);
    176   json_decref (wm->credit_facade_credentials);
    177   GNUNET_free (wm);
    178 }
    179 
    180 
    181 void
    182 TMH_instance_decref (struct TMH_MerchantInstance *mi)
    183 {
    184   struct TMH_WireMethod *wm;
    185 
    186   mi->rc--;
    187   if (0 != mi->rc)
    188     return;
    189   TMH_force_get_orders_resume (mi);
    190   while (NULL != (wm = (mi->wm_head)))
    191   {
    192     GNUNET_CONTAINER_DLL_remove (mi->wm_head,
    193                                  mi->wm_tail,
    194                                  wm);
    195     TMH_wire_method_free (wm);
    196   }
    197 
    198   GNUNET_free (mi->settings.id);
    199   GNUNET_free (mi->settings.name);
    200   GNUNET_free (mi->settings.email);
    201   GNUNET_free (mi->settings.phone);
    202   GNUNET_free (mi->settings.website);
    203   GNUNET_free (mi->settings.logo);
    204   json_decref (mi->settings.address);
    205   json_decref (mi->settings.jurisdiction);
    206   GNUNET_free (mi);
    207 }
    208 
    209 
    210 enum GNUNET_GenericReturnValue
    211 TMH_instance_free_cb (void *cls,
    212                       const struct GNUNET_HashCode *key,
    213                       void *value)
    214 {
    215   struct TMH_MerchantInstance *mi = value;
    216 
    217   (void) cls;
    218   (void) key;
    219   TMH_force_get_orders_resume (mi);
    220   GNUNET_assert (GNUNET_OK ==
    221                  GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
    222                                                        &mi->h_instance,
    223                                                        mi));
    224   TMH_instance_decref (mi);
    225   return GNUNET_YES;
    226 }
    227 
    228 
    229 /**
    230  * Shutdown task (invoked when the application is being
    231  * terminated for any reason)
    232  *
    233  * @param cls NULL
    234  */
    235 static void
    236 do_shutdown (void *cls)
    237 {
    238   (void) cls;
    239   TALER_MHD_daemons_halt ();
    240   TMH_handler_statistic_report_transactions_cleanup ();
    241   TMH_force_kac_resume ();
    242   TMH_force_orders_resume ();
    243   TMH_force_get_sessions_ID_resume ();
    244   TMH_force_get_orders_resume_typst ();
    245   TMH_force_ac_resume ();
    246   TMH_force_pc_resume ();
    247   TMH_force_kyc_resume ();
    248   TMH_force_gorc_resume ();
    249   TMH_force_wallet_get_order_resume ();
    250   TMH_force_wallet_refund_order_resume ();
    251   TMH_challenge_done ();
    252   if (NULL != instance_eh)
    253   {
    254     TALER_MERCHANTDB_event_listen_cancel (instance_eh);
    255     instance_eh = NULL;
    256   }
    257   TMH_EXCHANGES_done ();
    258   if (NULL != TMH_by_id_map)
    259   {
    260     GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
    261                                            &TMH_instance_free_cb,
    262                                            NULL);
    263     GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map);
    264     TMH_by_id_map = NULL;
    265   }
    266   TALER_MHD_daemons_destroy ();
    267   if (NULL != TMH_db)
    268   {
    269     TALER_MERCHANTDB_disconnect (TMH_db);
    270     TMH_db = NULL;
    271   }
    272   TALER_TEMPLATING_done ();
    273   if (NULL != TMH_curl_ctx)
    274   {
    275     GNUNET_CURL_fini (TMH_curl_ctx);
    276     TMH_curl_ctx = NULL;
    277   }
    278   if (NULL != merchant_curl_rc)
    279   {
    280     GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
    281     merchant_curl_rc = NULL;
    282   }
    283   if (NULL != TMH_payment_target_regex)
    284   {
    285     regfree (&TMH_payment_target_re);
    286     GNUNET_free (TMH_payment_target_regex);
    287   }
    288 }
    289 
    290 
    291 /**
    292  * Function called whenever MHD is done with a request.  If the
    293  * request was a POST, we may have stored a `struct Buffer *` in the
    294  * @a con_cls that might still need to be cleaned up.  Call the
    295  * respective function to free the memory.
    296  *
    297  * @param cls client-defined closure
    298  * @param connection connection handle
    299  * @param con_cls value as set by the last call to
    300  *        the #MHD_AccessHandlerCallback
    301  * @param toe reason for request termination
    302  * @see #MHD_OPTION_NOTIFY_COMPLETED
    303  * @ingroup request
    304  */
    305 static void
    306 handle_mhd_completion_callback (void *cls,
    307                                 struct MHD_Connection *connection,
    308                                 void **con_cls,
    309                                 enum MHD_RequestTerminationCode toe)
    310 {
    311   struct TMH_HandlerContext *hc = *con_cls;
    312 
    313   (void) cls;
    314   if (NULL == hc)
    315     return;
    316   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    317   {
    318 #if MHD_VERSION >= 0x00097304
    319     const union MHD_ConnectionInfo *ci;
    320     unsigned int http_status = 0;
    321 
    322     ci = MHD_get_connection_info (connection,
    323                                   MHD_CONNECTION_INFO_HTTP_STATUS);
    324     if (NULL != ci)
    325       http_status = ci->http_status;
    326     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    327                 "Request for `%s' completed with HTTP status %u (%d)\n",
    328                 hc->url,
    329                 http_status,
    330                 toe);
    331 #else
    332     (void) connection;
    333     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    334                 "Finished handling request for `%s' with MHD termination code %d\n",
    335                 hc->url,
    336                 (int) toe);
    337 #endif
    338   }
    339   if (NULL != hc->cc)
    340     hc->cc (hc->ctx);
    341   TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
    342   GNUNET_free (hc->infix);
    343   GNUNET_free (hc->rewritten_url);
    344   if (NULL != hc->request_body)
    345     json_decref (hc->request_body);
    346   if (NULL != hc->instance)
    347     TMH_instance_decref (hc->instance);
    348   TALER_MERCHANTDB_preflight (TMH_db);
    349   GNUNET_free (hc->full_url);
    350   GNUNET_free (hc);
    351   *con_cls = NULL;
    352 }
    353 
    354 
    355 struct TMH_MerchantInstance *
    356 TMH_lookup_instance (const char *instance_id)
    357 {
    358   struct GNUNET_HashCode h_instance;
    359   char *id;
    360 
    361   if (NULL == instance_id)
    362     id = GNUNET_strdup ("admin");
    363   else
    364     id = GNUNET_STRINGS_utf8_tolower (instance_id);
    365   GNUNET_CRYPTO_hash (id,
    366                       strlen (id),
    367                       &h_instance);
    368   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    369               "Looking for by-id key %s of '%s' in hashmap\n",
    370               GNUNET_h2s (&h_instance),
    371               id);
    372   GNUNET_free (id);
    373   /* We're fine if that returns NULL, the calling routine knows how
    374      to handle that */
    375   return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map,
    376                                             &h_instance);
    377 }
    378 
    379 
    380 /**
    381  * Add instance definition to our active set of instances.
    382  *
    383  * @param[in,out] mi merchant instance details to define
    384  * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already
    385  */
    386 enum GNUNET_GenericReturnValue
    387 TMH_add_instance (struct TMH_MerchantInstance *mi)
    388 {
    389   const char *id;
    390   enum GNUNET_GenericReturnValue ret;
    391 
    392   id = mi->settings.id;
    393   if (NULL == id)
    394     id = "admin";
    395   GNUNET_CRYPTO_hash (id,
    396                       strlen (id),
    397                       &mi->h_instance);
    398   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    399               "Looking for by-id key %s of `%s' in hashmap\n",
    400               GNUNET_h2s (&mi->h_instance),
    401               id);
    402   ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map,
    403                                            &mi->h_instance,
    404                                            mi,
    405                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
    406   if (GNUNET_OK == ret)
    407   {
    408     GNUNET_assert (mi->rc < UINT_MAX);
    409     mi->rc++;
    410   }
    411   return ret;
    412 }
    413 
    414 
    415 /**
    416  * Function called first by MHD with the full URL.
    417  *
    418  * @param cls NULL
    419  * @param full_url the full URL
    420  * @param con MHD connection object
    421  * @return our handler context
    422  */
    423 static void *
    424 full_url_track_callback (void *cls,
    425                          const char *full_url,
    426                          struct MHD_Connection *con)
    427 {
    428   struct TMH_HandlerContext *hc;
    429 
    430   hc = GNUNET_new (struct TMH_HandlerContext);
    431   hc->connection = con;
    432   GNUNET_async_scope_fresh (&hc->async_scope_id);
    433   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    434   hc->full_url = GNUNET_strdup (full_url);
    435   return hc;
    436 }
    437 
    438 
    439 /**
    440  * The callback was called again by MHD, continue processing
    441  * the request with the already identified handler.
    442  *
    443  * @param hc the handler context
    444  * @param upload_data the data being uploaded (excluding HEADERS,
    445  *        for a POST that fits into memory and that is encoded
    446  *        with a supported encoding, the POST data will NOT be
    447  *        given in upload_data and is instead available as
    448  *        part of #MHD_get_connection_values; very large POST
    449  *        data *will* be made available incrementally in
    450  *        @a upload_data)
    451  * @param upload_data_size set initially to the size of the
    452  *        @a upload_data provided; the method must update this
    453  *        value to the number of bytes NOT processed;
    454  * @return #MHD_YES if the connection was handled successfully,
    455  *         #MHD_NO if the socket must be closed due to a serious
    456  *         error while handling the request
    457  */
    458 static enum MHD_Result
    459 process_upload_with_handler (struct TMH_HandlerContext *hc,
    460                              const char *upload_data,
    461                              size_t *upload_data_size)
    462 {
    463   GNUNET_assert (NULL != hc->rh);
    464   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    465   if ( (hc->has_body) &&
    466        (NULL == hc->request_body) )
    467   {
    468     size_t mul = hc->rh->max_upload;
    469     enum GNUNET_GenericReturnValue res;
    470 
    471     if (0 == mul)
    472       mul = DEFAULT_MAX_UPLOAD_SIZE;
    473     if ( (hc->total_upload + *upload_data_size < hc->total_upload) ||
    474          (hc->total_upload + *upload_data_size > mul) )
    475     {
    476       /* Client exceeds upload limit. Should _usually_ be checked earlier
    477          when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with
    478          chunked encoding an uploader MAY have omitted this, and thus
    479          not permitted us to check on time. In this case, we just close
    480          the connection once it exceeds our limit (instead of waiting
    481          for the upload to complete and then fail). This could theoretically
    482          cause some clients to retry, alas broken or malicious clients
    483          are likely to retry anyway, so little we can do about it, and
    484          failing earlier seems the best option here.  */
    485       GNUNET_break_op (0);
    486       return MHD_NO;
    487     }
    488     hc->total_upload += *upload_data_size;
    489     res = TALER_MHD_parse_post_json (hc->connection,
    490                                      &hc->json_parse_context,
    491                                      upload_data,
    492                                      upload_data_size,
    493                                      &hc->request_body);
    494     if (GNUNET_SYSERR == res)
    495       return MHD_NO;
    496     /* A error response was already generated */
    497     if ( (GNUNET_NO == res) ||
    498          /* or, need more data to accomplish parsing */
    499          (NULL == hc->request_body) )
    500       return MHD_YES;   /* let MHD call us *again* */
    501   }
    502   /* Upload complete (if any), call handler to generate reply */
    503   return hc->rh->handler (hc->rh,
    504                           hc->connection,
    505                           hc);
    506 }
    507 
    508 
    509 /**
    510  * Log information about the request being handled.
    511  *
    512  * @param hc handler context
    513  * @param method HTTP method of the request
    514  */
    515 static void
    516 log_request (const struct TMH_HandlerContext *hc,
    517              const char *method)
    518 {
    519   const char *correlation_id;
    520 
    521   correlation_id = MHD_lookup_connection_value (hc->connection,
    522                                                 MHD_HEADER_KIND,
    523                                                 "Taler-Correlation-Id");
    524   if ( (NULL != correlation_id) &&
    525        (GNUNET_YES !=
    526         GNUNET_CURL_is_valid_scope_id (correlation_id)) )
    527   {
    528     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    529                 "Illegal incoming correlation ID\n");
    530     correlation_id = NULL;
    531   }
    532   if (NULL != correlation_id)
    533     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    534                 "Handling request for (%s) URL '%s', correlation_id=%s\n",
    535                 method,
    536                 hc->url,
    537                 correlation_id);
    538   else
    539     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    540                 "Handling request (%s) for URL '%s'\n",
    541                 method,
    542                 hc->url);
    543 }
    544 
    545 
    546 /**
    547  * Identify the instance of the request from the URL.
    548  *
    549  * @param[in,out] hc handler context
    550  * @param[in,out] urlp URL path of the request, updated to point to the rest
    551  * @param[out] use_admin set to true if we are using the admin instance
    552  * @return #GNUNET_OK on success,
    553  *         #GNUNET_NO if an error was queued (return #MHD_YES)
    554  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
    555  */
    556 static enum GNUNET_GenericReturnValue
    557 identify_instance (struct TMH_HandlerContext *hc,
    558                    const char **urlp,
    559                    bool *use_admin)
    560 {
    561   const char *url = *urlp;
    562   const char *instance_prefix = "/instances/";
    563 
    564   if (0 == strncmp (url,
    565                     instance_prefix,
    566                     strlen (instance_prefix)))
    567   {
    568     /* url starts with "/instances/" */
    569     const char *istart = url + strlen (instance_prefix);
    570     const char *slash = strchr (istart, '/');
    571     char *raw_id;
    572     char *instance_id;
    573 
    574     if (NULL == slash)
    575       raw_id = GNUNET_strdup (istart);
    576     else
    577       raw_id = GNUNET_strndup (istart,
    578                                slash - istart);
    579     /* Instance IDs are case-insensitive, so fold the segment before comparing
    580        it against "admin" below.  Without this, '/instances/Admin/' misses the
    581        redirect to the modern path, leaving use_admin false, and every
    582        'default_only' handler that '/instances/admin/' reaches (all of
    583        /management/) then replies 404. */
    584     instance_id = GNUNET_STRINGS_utf8_tolower (raw_id);
    585     GNUNET_free (raw_id);
    586     if (0 == strcmp (instance_id,
    587                      "admin"))
    588     {
    589       enum MHD_Result ret;
    590       struct MHD_Response *response;
    591       const char *rstart = hc->full_url + strlen (instance_prefix);
    592       const char *rslash = strchr (rstart, '/');
    593 
    594       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    595                   "Client used deprecated '/instances/admin/' path. Redirecting to modern path\n");
    596 
    597       response
    598         = MHD_create_response_from_buffer (0,
    599                                            NULL,
    600                                            MHD_RESPMEM_PERSISTENT);
    601       TALER_MHD_add_global_headers (response,
    602                                     true);
    603       if (MHD_NO ==
    604           MHD_add_response_header (response,
    605                                    MHD_HTTP_HEADER_LOCATION,
    606                                    NULL == rslash
    607                                      ? "/"
    608                                      : rslash))
    609       {
    610         GNUNET_break (0);
    611         MHD_destroy_response (response);
    612         GNUNET_free (instance_id);
    613         return GNUNET_SYSERR;
    614       }
    615       ret = MHD_queue_response (hc->connection,
    616                                 MHD_HTTP_PERMANENT_REDIRECT,
    617                                 response);
    618       MHD_destroy_response (response);
    619       GNUNET_free (instance_id);
    620       return (MHD_YES == ret) ? GNUNET_NO : GNUNET_SYSERR;
    621     }
    622     hc->instance = TMH_lookup_instance (instance_id);
    623     if ( (NULL == hc->instance) &&
    624          (0 == strcmp ("admin",
    625                        instance_id)) )
    626       hc->instance = TMH_lookup_instance (NULL);
    627     GNUNET_free (instance_id);
    628     if (NULL == slash)
    629       *urlp = "";
    630     else
    631       *urlp = slash;
    632   }
    633   else
    634   {
    635     /* use 'default' */
    636     *use_admin = true;
    637     hc->instance = TMH_lookup_instance (NULL);
    638   }
    639   if (NULL != hc->instance)
    640   {
    641     GNUNET_assert (hc->instance->rc < UINT_MAX);
    642     hc->instance->rc++;
    643   }
    644   return GNUNET_OK;
    645 }
    646 
    647 
    648 /**
    649  * A client has requested the given url using the given method
    650  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
    651  * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
    652  * must call MHD callbacks to provide content to give back to the
    653  * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
    654  * #MHD_HTTP_NOT_FOUND, etc.).
    655  *
    656  * @param cls argument given together with the function
    657  *        pointer when the handler was registered with MHD
    658  * @param connection the MHD connection to handle
    659  * @param url the requested url
    660  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
    661  *        #MHD_HTTP_METHOD_PUT, etc.)
    662  * @param version the HTTP version string (i.e.
    663  *        #MHD_HTTP_VERSION_1_1)
    664  * @param upload_data the data being uploaded (excluding HEADERS,
    665  *        for a POST that fits into memory and that is encoded
    666  *        with a supported encoding, the POST data will NOT be
    667  *        given in upload_data and is instead available as
    668  *        part of #MHD_get_connection_values; very large POST
    669  *        data *will* be made available incrementally in
    670  *        @a upload_data)
    671  * @param upload_data_size set initially to the size of the
    672  *        @a upload_data provided; the method must update this
    673  *        value to the number of bytes NOT processed;
    674  * @param con_cls pointer that the callback can set to some
    675  *        address and that will be preserved by MHD for future
    676  *        calls for this request; since the access handler may
    677  *        be called many times (i.e., for a PUT/POST operation
    678  *        with plenty of upload data) this allows the application
    679  *        to easily associate some request-specific state.
    680  *        If necessary, this state can be cleaned up in the
    681  *        global #MHD_RequestCompletedCallback (which
    682  *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
    683  *        Initially, `*con_cls` will be set up by the
    684  *        full_url_track_callback().
    685  * @return #MHD_YES if the connection was handled successfully,
    686  *         #MHD_NO if the socket must be closed due to a serious
    687  *         error while handling the request
    688  */
    689 static enum MHD_Result
    690 url_handler (void *cls,
    691              struct MHD_Connection *connection,
    692              const char *url,
    693              const char *method,
    694              const char *version,
    695              const char *upload_data,
    696              size_t *upload_data_size,
    697              void **con_cls)
    698 {
    699   struct TMH_HandlerContext *hc = *con_cls;
    700   bool use_admin = false;
    701   bool is_public = false;
    702 
    703   (void) cls;
    704   (void) version;
    705   if (NULL == hc->url)
    706   {
    707     /* First time.
    708      * Find out the merchant backend instance for the request.
    709      * If there is an instance, remove the instance specification
    710      * from the beginning of the request URL. */
    711     enum GNUNET_GenericReturnValue ret;
    712 
    713     hc->url = url;
    714     log_request (hc,
    715                  method);
    716     ret = identify_instance (hc,
    717                              &url,
    718                              &use_admin);
    719     if (GNUNET_OK != ret)
    720       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    721   }
    722 
    723   if (NULL != hc->instance)
    724   {
    725     /* Narrow DB interaction to selected instance */
    726     enum GNUNET_DB_QueryStatus qs;
    727 
    728     qs = TALER_MERCHANTDB_set_instance (TMH_db,
    729                                         hc->instance->settings.id);
    730     switch (qs)
    731     {
    732     case GNUNET_DB_STATUS_HARD_ERROR:
    733       GNUNET_break (0);
    734       return TALER_MHD_reply_with_error (
    735         connection,
    736         MHD_HTTP_INTERNAL_SERVER_ERROR,
    737         TALER_EC_GENERIC_DB_SETUP_FAILED,
    738         "set_instance");
    739     case GNUNET_DB_STATUS_SOFT_ERROR:
    740       GNUNET_break (0);
    741       return TALER_MHD_reply_with_error (
    742         connection,
    743         MHD_HTTP_INTERNAL_SERVER_ERROR,
    744         TALER_EC_GENERIC_DB_SETUP_FAILED,
    745         "set_instance");
    746     case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
    747       return TALER_MHD_reply_with_error (
    748         connection,
    749         MHD_HTTP_NOT_FOUND,
    750         TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    751         hc->url);
    752     case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
    753       break;
    754     }
    755   }
    756 
    757   if (NULL != hc->rh)
    758   {
    759     enum MHD_Result res;
    760 
    761     /* MHD calls us again for a request, we already identified
    762        the handler, just continue processing with the handler */
    763     res = process_upload_with_handler (hc,
    764                                        upload_data,
    765                                        upload_data_size);
    766     if (NULL != hc->instance)
    767     {
    768       GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    769                     TALER_MERCHANTDB_set_instance (TMH_db,
    770                                                    NULL));
    771     }
    772     return res;
    773   }
    774 
    775   /* First time, let's figure out the handler */
    776   {
    777     enum GNUNET_GenericReturnValue ret;
    778 
    779     ret = TMH_dispatch_request (hc,
    780                                 url,
    781                                 method,
    782                                 use_admin,
    783                                 &is_public);
    784     if (GNUNET_OK != ret)
    785     {
    786       if (NULL != hc->instance)
    787       {
    788         GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    789                       TALER_MERCHANTDB_set_instance (TMH_db,
    790                                                      NULL));
    791       }
    792       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    793     }
    794   }
    795 
    796   /* At this point, we must have found a handler */
    797   GNUNET_assert (NULL != hc->rh);
    798 
    799   /* If an instance must be there, check one exists */
    800   if ( (NULL == hc->instance) &&
    801        (! hc->rh->skip_instance) )
    802   {
    803     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    804                 "Instance for `%s' not known\n",
    805                 hc->url);
    806     return TALER_MHD_reply_with_error (connection,
    807                                        MHD_HTTP_NOT_FOUND,
    808                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
    809                                        hc->url);
    810   }
    811 
    812   /* Perform access control for non-public handlers */
    813   if (! is_public)
    814   {
    815     enum GNUNET_GenericReturnValue ret;
    816 
    817     ret = TMH_perform_access_control (hc);
    818     if (GNUNET_OK != ret)
    819     {
    820       if (NULL != hc->instance)
    821       {
    822         GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    823                       TALER_MERCHANTDB_set_instance (TMH_db,
    824                                                      NULL));
    825       }
    826       return (GNUNET_NO == ret) ? MHD_YES : MHD_NO;
    827     }
    828   }
    829   if (NULL != hc->instance)
    830   {
    831     GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    832                   TALER_MERCHANTDB_set_instance (TMH_db,
    833                                                  NULL));
    834   }
    835 
    836   if ( (NULL != hc->instance) && /* make static analysis happy */
    837        (! hc->rh->skip_instance) &&
    838        (hc->instance->deleted) &&
    839        (! hc->rh->allow_deleted_instance) )
    840   {
    841     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    842                 "Instance `%s' was deleted\n",
    843                 hc->instance->settings.id);
    844     return TALER_MHD_reply_with_error (connection,
    845                                        MHD_HTTP_NOT_FOUND,
    846                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED,
    847                                        hc->instance->settings.id);
    848   }
    849 
    850   /* Check upload constraints */
    851   hc->has_body = ( (0 == strcasecmp (method,
    852                                      MHD_HTTP_METHOD_POST)) ||
    853                    /* PUT is not yet used */
    854                    (0 == strcasecmp (method,
    855                                      MHD_HTTP_METHOD_PATCH)) );
    856   if (hc->has_body)
    857   {
    858     /* This is a macro: it will queue an error response and return
    859        from this function if the upload would be too large. */
    860     TALER_MHD_check_content_length (connection,
    861                                     0 == hc->rh->max_upload
    862                                     ? DEFAULT_MAX_UPLOAD_SIZE
    863                                     : hc->rh->max_upload);
    864     GNUNET_break (NULL == hc->request_body); /* can't have it already */
    865   }
    866   /* wait for MHD to call us again, this time hc->url will be non-NULL
    867      and we should jump straight into process_upload_with_handler(). */
    868   return MHD_YES;
    869 }
    870 
    871 
    872 /**
    873  * Callback invoked with information about a bank account.
    874  *
    875  * @param cls closure with a `struct TMH_MerchantInstance *`
    876  * @param merchant_priv private key of the merchant instance
    877  * @param acc details about the account
    878  */
    879 static void
    880 add_account_cb (void *cls,
    881                 const struct TALER_MerchantPrivateKeyP *merchant_priv,
    882                 const struct TALER_MERCHANTDB_AccountDetails *acc)
    883 {
    884   struct TMH_MerchantInstance *mi = cls;
    885   struct TMH_WireMethod *wm;
    886 
    887   (void) merchant_priv;
    888   wm = GNUNET_new (struct TMH_WireMethod);
    889   wm->h_wire = acc->h_wire;
    890   wm->payto_uri.full_payto
    891     = GNUNET_strdup (acc->payto_uri.full_payto);
    892   if (NULL != acc->extra_wire_subject_metadata)
    893     wm->extra_wire_subject_metadata
    894       = GNUNET_strdup (acc->extra_wire_subject_metadata);
    895   wm->wire_salt = acc->salt;
    896   wm->wire_method
    897     = TALER_payto_get_method (acc->payto_uri.full_payto);
    898   wm->active = acc->active;
    899   GNUNET_CONTAINER_DLL_insert (mi->wm_head,
    900                                mi->wm_tail,
    901                                wm);
    902 }
    903 
    904 
    905 /**
    906  * Function called during startup to add all known instances to our
    907  * hash map in memory for faster lookups when we receive requests.
    908  *
    909  * @param cls closure, NULL, unused
    910  * @param merchant_pub public key of the instance
    911  * @param merchant_priv private key of the instance, NULL if not available
    912  * @param is detailed configuration settings for the instance
    913  * @param ias authentication settings for the instance
    914  */
    915 static void
    916 add_instance_cb (void *cls,
    917                  const struct TALER_MerchantPublicKeyP *merchant_pub,
    918                  const struct TALER_MerchantPrivateKeyP *merchant_priv,
    919                  const struct TALER_MERCHANTDB_InstanceSettings *is,
    920                  const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
    921 {
    922   struct TMH_MerchantInstance *mi;
    923   enum GNUNET_DB_QueryStatus qs;
    924 
    925   (void) cls;
    926   mi = TMH_lookup_instance (is->id);
    927   if (NULL != mi)
    928   {
    929     /* (outdated) entry exists, remove old entry */
    930     (void) TMH_instance_free_cb (NULL,
    931                                  &mi->h_instance,
    932                                  mi);
    933   }
    934   mi = GNUNET_new (struct TMH_MerchantInstance);
    935   mi->settings = *is;
    936   mi->auth = *ias;
    937   mi->settings.id = GNUNET_STRINGS_utf8_tolower (mi->settings.id);
    938   mi->settings.name = GNUNET_strdup (mi->settings.name);
    939   if (NULL != mi->settings.email)
    940     mi->settings.email = GNUNET_strdup (mi->settings.email);
    941   if (NULL != mi->settings.phone)
    942     mi->settings.phone = GNUNET_strdup (mi->settings.phone);
    943   if (NULL != mi->settings.website)
    944     mi->settings.website = GNUNET_strdup (mi->settings.website);
    945   if (NULL != mi->settings.logo)
    946     mi->settings.logo = GNUNET_strdup (mi->settings.logo);
    947   mi->settings.address = json_incref (mi->settings.address);
    948   mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
    949   if (NULL != merchant_priv)
    950     mi->merchant_priv = *merchant_priv;
    951   else
    952     mi->deleted = true;
    953   mi->merchant_pub = *merchant_pub;
    954   qs = TALER_MERCHANTDB_set_instance (TMH_db,
    955                                       mi->settings.id);
    956   if (0 > qs)
    957   {
    958     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    959                 "Error setting instance `%s'\n",
    960                 mi->settings.id);
    961     GNUNET_SCHEDULER_shutdown ();
    962     return;
    963   }
    964   qs = TALER_MERCHANTDB_iterate_accounts_by_instance (
    965     TMH_db,
    966     mi->settings.id,
    967     &add_account_cb,
    968     mi);
    969   if (0 > qs)
    970   {
    971     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    972                 "Error loading accounts of `%s' from database\n",
    973                 mi->settings.id);
    974     GNUNET_SCHEDULER_shutdown ();
    975     return;
    976   }
    977   GNUNET_assert (GNUNET_OK ==
    978                  TMH_add_instance (mi));
    979   GNUNET_break (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT ==
    980                 TALER_MERCHANTDB_set_instance (TMH_db,
    981                                                NULL));
    982 }
    983 
    984 
    985 /**
    986  * Trigger (re)loading of instance settings from DB.
    987  *
    988  * @param cls NULL
    989  * @param extra ID of the instance that changed, NULL
    990  *              to load all instances (will not handle purges!)
    991  * @param extra_len number of bytes in @a extra
    992  */
    993 static void
    994 load_instances (void *cls,
    995                 const void *extra,
    996                 size_t extra_len)
    997 {
    998   enum GNUNET_DB_QueryStatus qs;
    999   const char *id = extra;
   1000 
   1001   (void) cls;
   1002   if ( (NULL != extra) &&
   1003        ( (0 == extra_len) ||
   1004          ('\0' != id[extra_len - 1]) ) )
   1005   {
   1006     GNUNET_break (0 == extra_len);
   1007     extra = NULL;
   1008   }
   1009   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1010               "Received instance settings notification: reload `%s'\n",
   1011               id);
   1012   if (NULL == extra)
   1013   {
   1014     qs = TALER_MERCHANTDB_iterate_instances (TMH_db,
   1015                                              false,
   1016                                              &add_instance_cb,
   1017                                              NULL);
   1018   }
   1019   else
   1020   {
   1021     struct TMH_MerchantInstance *mi;
   1022 
   1023     /* This must be done here to handle instance
   1024        purging, as for purged instances, the DB
   1025        lookup below will otherwise do nothing */
   1026     mi = TMH_lookup_instance (id);
   1027     if (NULL != mi)
   1028     {
   1029       (void) TMH_instance_free_cb (NULL,
   1030                                    &mi->h_instance,
   1031                                    mi);
   1032     }
   1033     qs = TALER_MERCHANTDB_iterate_instances_by_id (TMH_db,
   1034                                                    id,
   1035                                                    false,
   1036                                                    &add_instance_cb,
   1037                                                    NULL);
   1038   }
   1039   if (0 > qs)
   1040   {
   1041     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1042                 "Failed initialization. Check database setup.\n");
   1043     global_ret = EXIT_NOPERMISSION;
   1044     GNUNET_SCHEDULER_shutdown ();
   1045     return;
   1046   }
   1047 }
   1048 
   1049 
   1050 /**
   1051  * A transaction modified an instance setting (or created/deleted/purged
   1052  * one). Notify all backends about the change.
   1053  *
   1054  * @param id ID of the instance that changed
   1055  */
   1056 void
   1057 TMH_reload_instances (const char *id)
   1058 {
   1059   struct GNUNET_DB_EventHeaderP es = {
   1060     .size = htons (sizeof (es)),
   1061     .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
   1062   };
   1063 
   1064   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1065               "Generating instance settings notification: reload `%s'\n",
   1066               id);
   1067   TALER_MERCHANTDB_event_notify (TMH_db,
   1068                                  &es,
   1069                                  id,
   1070                                  (NULL == id)
   1071                         ? 0
   1072                         : strlen (id) + 1);
   1073 }
   1074 
   1075 
   1076 /**
   1077  * Callback invoked on every listen socket to start the
   1078  * respective MHD HTTP daemon.
   1079  *
   1080  * @param cls unused
   1081  * @param lsock the listen socket
   1082  */
   1083 static void
   1084 start_daemon (void *cls,
   1085               int lsock)
   1086 {
   1087   struct MHD_Daemon *mhd;
   1088 
   1089   (void) cls;
   1090   GNUNET_assert (-1 != lsock);
   1091   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK
   1092                           | MHD_USE_AUTO,
   1093                           0 /* port */,
   1094                           NULL, NULL,
   1095                           &url_handler, NULL,
   1096                           MHD_OPTION_LISTEN_SOCKET, lsock,
   1097                           MHD_OPTION_URI_LOG_CALLBACK,
   1098                           &full_url_track_callback, NULL,
   1099                           MHD_OPTION_NOTIFY_COMPLETED,
   1100                           &handle_mhd_completion_callback, NULL,
   1101                           MHD_OPTION_CONNECTION_TIMEOUT,
   1102                           (unsigned int) 10 /* 10s */,
   1103                           MHD_OPTION_END);
   1104   if (NULL == mhd)
   1105   {
   1106     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1107                 "Failed to launch HTTP service.\n");
   1108     GNUNET_SCHEDULER_shutdown ();
   1109     return;
   1110   }
   1111   have_daemons = true;
   1112   TALER_MHD_daemon_start (mhd);
   1113 }
   1114 
   1115 
   1116 /**
   1117  * Main function that will be run by the scheduler.
   1118  *
   1119  * @param cls closure
   1120  * @param args remaining command-line arguments
   1121  * @param cfgfile name of the configuration file used (for saving, can be
   1122  *        NULL!)
   1123  * @param config configuration
   1124  */
   1125 static void
   1126 run (void *cls,
   1127      char *const *args,
   1128      const char *cfgfile,
   1129      const struct GNUNET_CONFIGURATION_Handle *config)
   1130 {
   1131   enum TALER_MHD_GlobalOptions go;
   1132   int elen;
   1133 
   1134   (void) cls;
   1135   (void) args;
   1136   (void) cfgfile;
   1137   TMH_cfg = config;
   1138   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1139               "Starting taler-merchant-httpd\n");
   1140   go = TALER_MHD_GO_NONE;
   1141   if (merchant_connection_close)
   1142     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
   1143   TALER_MHD_setup (go);
   1144   TALER_EXCHANGE_setup (enable_h3
   1145                         ? TALER_EXCHANGE_GO_ENABLE_HTTP3
   1146                         : TALER_EXCHANGE_GO_FORCE_HTTP1_1);
   1147   DONAU_setup (enable_h3
   1148                ? DONAU_GO_ENABLE_HTTP3
   1149                : DONAU_GO_FORCE_HTTP1_1);
   1150 
   1151   global_ret = EXIT_SUCCESS;
   1152   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   1153                                  NULL);
   1154 
   1155   TMH_curl_ctx
   1156     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   1157                         &merchant_curl_rc);
   1158   if (NULL == TMH_curl_ctx)
   1159   {
   1160     GNUNET_break (0);
   1161     global_ret = EXIT_NO_RESTART;
   1162     GNUNET_SCHEDULER_shutdown ();
   1163     return;
   1164   }
   1165   merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx);
   1166   /* Disable 100 continue processing */
   1167   GNUNET_break (GNUNET_OK ==
   1168                 GNUNET_CURL_append_header (TMH_curl_ctx,
   1169                                            MHD_HTTP_HEADER_EXPECT ":"));
   1170   GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx,
   1171                                          "Taler-Correlation-Id");
   1172 
   1173   if (GNUNET_SYSERR ==
   1174       TALER_config_get_currency (TMH_cfg,
   1175                                  "merchant",
   1176                                  &TMH_currency))
   1177   {
   1178     global_ret = EXIT_NOTCONFIGURED;
   1179     GNUNET_SCHEDULER_shutdown ();
   1180     return;
   1181   }
   1182   if (GNUNET_OK !=
   1183       TALER_CONFIG_parse_currencies (TMH_cfg,
   1184                                      TMH_currency,
   1185                                      &TMH_num_cspecs,
   1186                                      &TMH_cspecs))
   1187   {
   1188     global_ret = EXIT_NOTCONFIGURED;
   1189     GNUNET_SCHEDULER_shutdown ();
   1190     return;
   1191   }
   1192 
   1193   {
   1194     char *spa_data;
   1195 
   1196     if (GNUNET_OK ==
   1197         GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1198                                                "merchant",
   1199                                                "GLOBAL_SPA_CONFIG_DATA",
   1200                                                &spa_data))
   1201     {
   1202       json_error_t err;
   1203 
   1204       TMH_global_spa_config_data = json_loads (spa_data,
   1205                                                JSON_REJECT_DUPLICATES,
   1206                                                &err);
   1207       GNUNET_free (spa_data);
   1208       if (NULL == TMH_global_spa_config_data)
   1209       {
   1210         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1211                                    "merchant",
   1212                                    "GLOBAL_SPA_CONFIG_DATA",
   1213                                    err.text);
   1214         global_ret = EXIT_NOTCONFIGURED;
   1215         GNUNET_SCHEDULER_shutdown ();
   1216         return;
   1217       }
   1218     }
   1219   }
   1220 
   1221 
   1222   if (GNUNET_SYSERR ==
   1223       (TMH_strict_v19
   1224          = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1225                                                  "merchant",
   1226                                                  "STRICT_PROTOCOL_V19")))
   1227   {
   1228     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1229                                "merchant",
   1230                                "STRICT_PROTOCOL_V19");
   1231     TMH_strict_v19 = GNUNET_NO;
   1232   }
   1233   if (GNUNET_SYSERR ==
   1234       (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1235                                                                  "merchant",
   1236                                                                  "DISABLE_AUTHENTICATION")))
   1237   {
   1238     TMH_auth_disabled = GNUNET_NO;
   1239   }
   1240   if (GNUNET_YES == TMH_auth_disabled)
   1241   {
   1242     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1243                 "DANGEROUS: Endpoint Authentication disabled!");
   1244   }
   1245 
   1246   if (GNUNET_SYSERR ==
   1247       (TMH_have_self_provisioning
   1248          = GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1249                                                  "merchant",
   1250                                                  "ENABLE_SELF_PROVISIONING")))
   1251   {
   1252     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1253                                "merchant",
   1254                                "ENABLE_SELF_PROVISIONING");
   1255     TMH_have_self_provisioning = GNUNET_NO;
   1256   }
   1257 
   1258   if (GNUNET_OK !=
   1259       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1260                                            "merchant",
   1261                                            "LEGAL_PRESERVATION",
   1262                                            &TMH_legal_expiration))
   1263   {
   1264     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   1265                                "merchant",
   1266                                "LEGAL_PRESERVATION");
   1267     global_ret = EXIT_NOTCONFIGURED;
   1268     GNUNET_SCHEDULER_shutdown ();
   1269     return;
   1270   }
   1271 
   1272   if (GNUNET_OK !=
   1273       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1274                                            "merchant",
   1275                                            "DEFAULT_PAY_DELAY",
   1276                                            &TMH_default_pay_delay))
   1277   {
   1278     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1279                                "merchant",
   1280                                "DEFAULT_PAY_DELAY");
   1281     TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS;
   1282   }
   1283   if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay))
   1284   {
   1285     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   1286                                "merchant",
   1287                                "DEFAULT_PAY_DELAY",
   1288                                "forever is not allowed");
   1289     global_ret = EXIT_NOTCONFIGURED;
   1290     GNUNET_SCHEDULER_shutdown ();
   1291     return;
   1292   }
   1293   if (GNUNET_OK !=
   1294       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1295                                            "merchant",
   1296                                            "DEFAULT_REFUND_DELAY",
   1297                                            &TMH_default_refund_delay))
   1298   {
   1299     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1300                                "merchant",
   1301                                "DEFAULT_REFUND_DELAY");
   1302     TMH_default_refund_delay = GNUNET_TIME_relative_multiply (
   1303       GNUNET_TIME_UNIT_DAYS,
   1304       15);
   1305   }
   1306   if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay))
   1307   {
   1308     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   1309                                "merchant",
   1310                                "DEFAULT_REFUND_DELAY",
   1311                                "forever is not allowed");
   1312     global_ret = EXIT_NOTCONFIGURED;
   1313     GNUNET_SCHEDULER_shutdown ();
   1314     return;
   1315   }
   1316 
   1317   if (GNUNET_OK !=
   1318       GNUNET_CONFIGURATION_get_value_time (TMH_cfg,
   1319                                            "merchant",
   1320                                            "DEFAULT_WIRE_TRANSFER_DELAY",
   1321                                            &TMH_default_wire_transfer_delay))
   1322   {
   1323     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1324                                "merchant",
   1325                                "DEFAULT_WIRE_TRANSFER_DELAY");
   1326     TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS;
   1327   }
   1328   if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay))
   1329   {
   1330     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   1331                                "merchant",
   1332                                "DEFAULT_WIRE_TRANSFER_DELAY",
   1333                                "forever is not allowed");
   1334     global_ret = EXIT_NOTCONFIGURED;
   1335     GNUNET_SCHEDULER_shutdown ();
   1336     return;
   1337   }
   1338 
   1339   {
   1340     char *dwtri;
   1341 
   1342     if (GNUNET_OK !=
   1343         GNUNET_CONFIGURATION_get_value_string (
   1344           TMH_cfg,
   1345           "merchant",
   1346           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
   1347           &dwtri))
   1348     {
   1349       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   1350                                  "merchant",
   1351                                  "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL");
   1352       TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE;
   1353     }
   1354     else
   1355     {
   1356       if (GNUNET_OK !=
   1357           GNUNET_TIME_string_to_round_interval (
   1358             dwtri,
   1359             &TMH_default_wire_transfer_rounding_interval))
   1360       {
   1361         GNUNET_log_config_invalid (
   1362           GNUNET_ERROR_TYPE_ERROR,
   1363           "merchant",
   1364           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
   1365           "invalid time rounding interval");
   1366         global_ret = EXIT_NOTCONFIGURED;
   1367         GNUNET_free (dwtri);
   1368         GNUNET_SCHEDULER_shutdown ();
   1369         return;
   1370       }
   1371       GNUNET_free (dwtri);
   1372     }
   1373   }
   1374 
   1375   TMH_load_terms (TMH_cfg);
   1376 
   1377   if (GNUNET_OK !=
   1378       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1379                                              "merchant",
   1380                                              "PAYMENT_TARGET_TYPES",
   1381                                              &TMH_allowed_payment_targets))
   1382   {
   1383     TMH_allowed_payment_targets = GNUNET_strdup ("*");
   1384   }
   1385 
   1386   if (GNUNET_OK !=
   1387       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1388                                              "merchant",
   1389                                              "DEFAULT_PERSONA",
   1390                                              &TMH_default_persona))
   1391   {
   1392     TMH_default_persona = GNUNET_strdup ("expert");
   1393   }
   1394 
   1395   if (GNUNET_OK !=
   1396       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1397                                              "merchant",
   1398                                              "PAYMENT_TARGET_REGEX",
   1399                                              &TMH_payment_target_regex))
   1400   {
   1401     TMH_payment_target_regex = NULL;
   1402   }
   1403   else
   1404   {
   1405     if (0 == strlen (TMH_payment_target_regex))
   1406     {
   1407       GNUNET_free (TMH_payment_target_regex);
   1408     }
   1409     else
   1410     {
   1411       if (0 != regcomp (&TMH_payment_target_re,
   1412                         TMH_payment_target_regex,
   1413                         REG_NOSUB | REG_EXTENDED))
   1414       {
   1415         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1416                                    "merchant",
   1417                                    "PAYMENT_TARGET_REGEX",
   1418                                    "malformed regular expression");
   1419         global_ret = EXIT_NOTCONFIGURED;
   1420         GNUNET_free (TMH_payment_target_regex);
   1421         GNUNET_SCHEDULER_shutdown ();
   1422         return;
   1423       }
   1424     }
   1425   }
   1426   if (GNUNET_OK !=
   1427       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1428                                              "merchant",
   1429                                              "PHONE_REGEX",
   1430                                              &TMH_phone_regex))
   1431   {
   1432     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   1433                                "merchant",
   1434                                "PHONE_REGEX",
   1435                                "no restrictions on phone number specified");
   1436   }
   1437   else
   1438   {
   1439     if (0 != regcomp (&TMH_phone_rx,
   1440                       TMH_phone_regex,
   1441                       REG_EXTENDED))
   1442     {
   1443       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1444                                  "merchant",
   1445                                  "PHONE_REGEX",
   1446                                  "Invalid regex specified");
   1447       global_ret = EXIT_NOTCONFIGURED;
   1448       GNUNET_SCHEDULER_shutdown ();
   1449       return;
   1450     }
   1451   }
   1452 
   1453   if (GNUNET_OK !=
   1454       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1455                                              "merchant",
   1456                                              "HELPER_SMS",
   1457                                              &TMH_helper_sms))
   1458   {
   1459     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   1460                                "merchant",
   1461                                "HELPER_SMS",
   1462                                "no helper specified");
   1463   }
   1464 
   1465   if (GNUNET_OK !=
   1466       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1467                                              "merchant",
   1468                                              "HELPER_EMAIL",
   1469                                              &TMH_helper_email))
   1470   {
   1471     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   1472                                "merchant",
   1473                                "HELPER_EMAIL",
   1474                                "no helper specified");
   1475   }
   1476 
   1477   {
   1478     char *tan_channels;
   1479 
   1480     if (GNUNET_OK ==
   1481         GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1482                                                "merchant",
   1483                                                "MANDATORY_TAN_CHANNELS",
   1484                                                &tan_channels))
   1485     {
   1486       for (char *tok = strtok (tan_channels,
   1487                                " ");
   1488            NULL != tok;
   1489            tok = strtok (NULL,
   1490                          " "))
   1491       {
   1492         if (0 == strcasecmp (tok,
   1493                              "sms"))
   1494         {
   1495           if (NULL == TMH_helper_sms)
   1496           {
   1497             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1498                                        "merchant",
   1499                                        "MANDATORY_TAN_CHANNELS",
   1500                                        "SMS mandatory, but no HELPER_SMS configured");
   1501             global_ret = EXIT_NOTCONFIGURED;
   1502             GNUNET_SCHEDULER_shutdown ();
   1503             GNUNET_free (tan_channels);
   1504             return;
   1505           }
   1506           TEH_mandatory_tan_channels |= TMH_TCS_SMS;
   1507         }
   1508         else if (0 == strcasecmp (tok,
   1509                                   "email"))
   1510         {
   1511           if (NULL == TMH_helper_email)
   1512           {
   1513             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1514                                        "merchant",
   1515                                        "MANDATORY_TAN_CHANNELS",
   1516                                        "EMAIL mandatory, but no HELPER_EMAIL configured");
   1517             global_ret = EXIT_NOTCONFIGURED;
   1518             GNUNET_SCHEDULER_shutdown ();
   1519             GNUNET_free (tan_channels);
   1520             return;
   1521           }
   1522           TEH_mandatory_tan_channels |= TMH_TCS_EMAIL;
   1523         }
   1524         else
   1525         {
   1526           GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1527                                      "merchant",
   1528                                      "MANDATORY_TAN_CHANNELS",
   1529                                      tok);
   1530           global_ret = EXIT_NOTCONFIGURED;
   1531           GNUNET_SCHEDULER_shutdown ();
   1532           GNUNET_free (tan_channels);
   1533           return;
   1534         }
   1535       }
   1536       GNUNET_free (tan_channels);
   1537     }
   1538   }
   1539 
   1540   if (GNUNET_OK ==
   1541       GNUNET_CONFIGURATION_get_value_string (TMH_cfg,
   1542                                              "merchant",
   1543                                              "BASE_URL",
   1544                                              &TMH_base_url))
   1545   {
   1546     if (! TALER_is_web_url (TMH_base_url))
   1547     {
   1548       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   1549                                  "merchant",
   1550                                  "BASE_URL",
   1551                                  "Needs to start with 'http://' or 'https://'");
   1552       global_ret = EXIT_NOTCONFIGURED;
   1553       GNUNET_SCHEDULER_shutdown ();
   1554       return;
   1555     }
   1556   }
   1557   if (GNUNET_OK ==
   1558       GNUNET_CONFIGURATION_get_value_filename (TMH_cfg,
   1559                                                "merchant",
   1560                                                "BACKOFFICE_SPA_DIR",
   1561                                                &TMH_spa_dir))
   1562   {
   1563     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1564                 "Loading merchant SPA from %s\n",
   1565                 TMH_spa_dir);
   1566   }
   1567   else
   1568   {
   1569     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   1570                 "Loading merchant SPA from default location\n");
   1571   }
   1572 
   1573   if (GNUNET_YES ==
   1574       GNUNET_CONFIGURATION_get_value_yesno (TMH_cfg,
   1575                                             "merchant",
   1576                                             "FORCE_AUDIT"))
   1577     TMH_force_audit = GNUNET_YES;
   1578   if (GNUNET_OK !=
   1579       TALER_TEMPLATING_init (TALER_MERCHANT_project_data ()))
   1580   {
   1581     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1582                 "Failed to setup templates\n");
   1583     global_ret = EXIT_NOTINSTALLED;
   1584     GNUNET_SCHEDULER_shutdown ();
   1585     return;
   1586   }
   1587   if (GNUNET_OK !=
   1588       TMH_spa_init (TMH_spa_dir))
   1589   {
   1590     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1591                 "Failed to load single page app\n");
   1592     global_ret = EXIT_NOTINSTALLED;
   1593     GNUNET_SCHEDULER_shutdown ();
   1594     return;
   1595   }
   1596   /* /static/ is currently not used */
   1597   /* (void) TMH_statics_init (); */
   1598   if (NULL ==
   1599       (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4,
   1600                                                              GNUNET_YES)))
   1601   {
   1602     global_ret = EXIT_FAILURE;
   1603     GNUNET_SCHEDULER_shutdown ();
   1604     return;
   1605   }
   1606   if (NULL ==
   1607       (TMH_db = TALER_MERCHANTDB_connect (TMH_cfg)))
   1608   {
   1609     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1610                 "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
   1611     global_ret = EXIT_FAILURE;
   1612     GNUNET_SCHEDULER_shutdown ();
   1613     return;
   1614   }
   1615   elen = TMH_EXCHANGES_init (config);
   1616   if (GNUNET_SYSERR == elen)
   1617   {
   1618     global_ret = EXIT_NOTCONFIGURED;
   1619     GNUNET_SCHEDULER_shutdown ();
   1620     return;
   1621   }
   1622   if (0 == elen)
   1623   {
   1624     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   1625                 "Fatal: no trusted exchanges configured. Exiting.\n");
   1626     global_ret = EXIT_NOTCONFIGURED;
   1627     GNUNET_SCHEDULER_shutdown ();
   1628     return;
   1629   }
   1630 
   1631   {
   1632     struct GNUNET_DB_EventHeaderP es = {
   1633       .size = htons (sizeof (es)),
   1634       .type = htons (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
   1635     };
   1636 
   1637     instance_eh = TALER_MERCHANTDB_event_listen (TMH_db,
   1638                                                  &es,
   1639                                                  GNUNET_TIME_UNIT_FOREVER_REL,
   1640                                                  &load_instances,
   1641                                                  NULL);
   1642   }
   1643   load_instances (NULL,
   1644                   NULL,
   1645                   0);
   1646   {
   1647     enum GNUNET_GenericReturnValue ret;
   1648 
   1649     ret = TALER_MHD_listen_bind (TMH_cfg,
   1650                                  "merchant",
   1651                                  &start_daemon,
   1652                                  NULL);
   1653     switch (ret)
   1654     {
   1655     case GNUNET_SYSERR:
   1656       global_ret = EXIT_NOTCONFIGURED;
   1657       GNUNET_SCHEDULER_shutdown ();
   1658       return;
   1659     case GNUNET_NO:
   1660       if (! have_daemons)
   1661       {
   1662         global_ret = EXIT_NOTCONFIGURED;
   1663         GNUNET_SCHEDULER_shutdown ();
   1664         return;
   1665       }
   1666       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1667                   "Could not open all configured listen sockets\n");
   1668       break;
   1669     case GNUNET_OK:
   1670       break;
   1671     }
   1672   }
   1673   global_ret = EXIT_SUCCESS;
   1674 }
   1675 
   1676 
   1677 /**
   1678  * The main function of the serve tool
   1679  *
   1680  * @param argc number of arguments from the command line
   1681  * @param argv command line arguments
   1682  * @return 0 ok, non-zero on error
   1683  */
   1684 int
   1685 main (int argc,
   1686       char *const *argv)
   1687 {
   1688   enum GNUNET_GenericReturnValue res;
   1689   struct GNUNET_GETOPT_CommandLineOption options[] = {
   1690     GNUNET_GETOPT_option_flag ('C',
   1691                                "connection-close",
   1692                                "force HTTP connections to be closed after each request",
   1693                                &merchant_connection_close),
   1694     GNUNET_GETOPT_option_flag ('3',
   1695                                "http3",
   1696                                "enable support for HTTP/2 and HTTP/3",
   1697                                &enable_h3),
   1698     GNUNET_GETOPT_option_timetravel ('T',
   1699                                      "timetravel"),
   1700     GNUNET_GETOPT_option_version (PACKAGE_VERSION),
   1701     GNUNET_GETOPT_OPTION_END
   1702   };
   1703 
   1704   res = GNUNET_PROGRAM_run (
   1705     TALER_MERCHANT_project_data (),
   1706     argc, argv,
   1707     "taler-merchant-httpd",
   1708     "Taler merchant's HTTP backend interface",
   1709     options,
   1710     &run, NULL);
   1711   if (GNUNET_SYSERR == res)
   1712     return EXIT_INVALIDARGUMENT;
   1713   if (GNUNET_NO == res)
   1714     return EXIT_SUCCESS;
   1715   return global_ret;
   1716 }