merchant

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

taler-merchant-httpd.c (50981B)


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