merchant

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

taler-merchant-httpd.c (56288B)


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