merchant

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

taler-merchant-httpd.c (51114B)


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