merchant

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

taler-merchant-httpd.c (55461B)


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