merchant

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

taler-merchant-httpd.c (51484B)


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