merchant

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

taler-merchant-httpd.c (56726B)


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