merchant

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

taler-merchant-httpd.c (107096B)


      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 "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_merchant_util.h"
     32 #include "taler-merchant-httpd_config.h"
     33 #include "taler-merchant-httpd_exchanges.h"
     34 #include "taler-merchant-httpd_get-orders-ID.h"
     35 #include "taler-merchant-httpd_get-products-image.h"
     36 #include "taler-merchant-httpd_get-templates-ID.h"
     37 #include "taler-merchant-httpd_helper.h"
     38 #include "taler-merchant-httpd_mhd.h"
     39 #include "taler-merchant-httpd_mfa.h"
     40 #include "taler-merchant-httpd_private-delete-account-ID.h"
     41 #include "taler-merchant-httpd_private-delete-categories-ID.h"
     42 #include "taler-merchant-httpd_private-delete-units-ID.h"
     43 #include "taler-merchant-httpd_private-delete-instances-ID.h"
     44 #include "taler-merchant-httpd_private-delete-instances-ID-token.h"
     45 #include "taler-merchant-httpd_private-delete-products-ID.h"
     46 #include "taler-merchant-httpd_private-delete-orders-ID.h"
     47 #include "taler-merchant-httpd_private-delete-otp-devices-ID.h"
     48 #include "taler-merchant-httpd_private-delete-templates-ID.h"
     49 #include "taler-merchant-httpd_private-delete-token-families-SLUG.h"
     50 #include "taler-merchant-httpd_private-delete-transfers-ID.h"
     51 #include "taler-merchant-httpd_private-delete-webhooks-ID.h"
     52 #include "taler-merchant-httpd_private-get-accounts.h"
     53 #include "taler-merchant-httpd_private-get-accounts-ID.h"
     54 #include "taler-merchant-httpd_private-get-categories.h"
     55 #include "taler-merchant-httpd_private-get-categories-ID.h"
     56 #include "taler-merchant-httpd_private-get-units.h"
     57 #include "taler-merchant-httpd_private-get-units-ID.h"
     58 #include "taler-merchant-httpd_private-get-incoming.h"
     59 #include "taler-merchant-httpd_private-get-instances.h"
     60 #include "taler-merchant-httpd_private-get-instances-ID.h"
     61 #include "taler-merchant-httpd_private-get-instances-ID-kyc.h"
     62 #include "taler-merchant-httpd_private-get-instances-ID-tokens.h"
     63 #include "taler-merchant-httpd_private-get-pos.h"
     64 #include "taler-merchant-httpd_private-get-products.h"
     65 #include "taler-merchant-httpd_private-get-products-ID.h"
     66 #include "taler-merchant-httpd_private-get-orders.h"
     67 #include "taler-merchant-httpd_private-get-orders-ID.h"
     68 #include "taler-merchant-httpd_private-get-otp-devices.h"
     69 #include "taler-merchant-httpd_private-get-otp-devices-ID.h"
     70 #include "taler-merchant-httpd_private-get-statistics-amount-SLUG.h"
     71 #include "taler-merchant-httpd_private-get-statistics-counter-SLUG.h"
     72 #include "taler-merchant-httpd_private-get-templates.h"
     73 #include "taler-merchant-httpd_private-get-templates-ID.h"
     74 #include "taler-merchant-httpd_private-get-token-families.h"
     75 #include "taler-merchant-httpd_private-get-token-families-SLUG.h"
     76 #include "taler-merchant-httpd_private-get-transfers.h"
     77 #include "taler-merchant-httpd_private-get-webhooks.h"
     78 #include "taler-merchant-httpd_private-get-webhooks-ID.h"
     79 #include "taler-merchant-httpd_private-patch-accounts-ID.h"
     80 #include "taler-merchant-httpd_private-patch-categories-ID.h"
     81 #include "taler-merchant-httpd_private-patch-units-ID.h"
     82 #include "taler-merchant-httpd_private-patch-instances-ID.h"
     83 #include "taler-merchant-httpd_private-patch-orders-ID-forget.h"
     84 #include "taler-merchant-httpd_private-patch-otp-devices-ID.h"
     85 #include "taler-merchant-httpd_private-patch-products-ID.h"
     86 #include "taler-merchant-httpd_private-patch-templates-ID.h"
     87 #include "taler-merchant-httpd_private-patch-token-families-SLUG.h"
     88 #include "taler-merchant-httpd_private-patch-webhooks-ID.h"
     89 #include "taler-merchant-httpd_private-post-account.h"
     90 #include "taler-merchant-httpd_private-post-categories.h"
     91 #include "taler-merchant-httpd_private-post-units.h"
     92 #include "taler-merchant-httpd_private-post-instances.h"
     93 #include "taler-merchant-httpd_private-post-instances-ID-auth.h"
     94 #include "taler-merchant-httpd_private-post-instances-ID-token.h"
     95 #include "taler-merchant-httpd_private-post-otp-devices.h"
     96 #include "taler-merchant-httpd_private-post-orders.h"
     97 #include "taler-merchant-httpd_private-post-orders-ID-refund.h"
     98 #include "taler-merchant-httpd_private-post-products.h"
     99 #include "taler-merchant-httpd_private-post-products-ID-lock.h"
    100 #include "taler-merchant-httpd_private-post-templates.h"
    101 #include "taler-merchant-httpd_private-post-token-families.h"
    102 #include "taler-merchant-httpd_private-post-transfers.h"
    103 #include "taler-merchant-httpd_private-post-webhooks.h"
    104 #include "taler-merchant-httpd_post-challenge-ID.h"
    105 #include "taler-merchant-httpd_post-challenge-ID-confirm.h"
    106 #include "taler-merchant-httpd_post-orders-ID-abort.h"
    107 #include "taler-merchant-httpd_post-orders-ID-claim.h"
    108 #include "taler-merchant-httpd_post-orders-ID-paid.h"
    109 #include "taler-merchant-httpd_post-orders-ID-pay.h"
    110 #include "taler-merchant-httpd_post-using-templates.h"
    111 #include "taler-merchant-httpd_post-orders-ID-refund.h"
    112 #include "taler-merchant-httpd_spa.h"
    113 #include "taler-merchant-httpd_statics.h"
    114 #include "taler-merchant-httpd_terms.h"
    115 
    116 #ifdef HAVE_DONAU_DONAU_SERVICE_H
    117 #include "taler-merchant-httpd_private-get-donau-instances.h"
    118 #include "taler-merchant-httpd_private-post-donau-instance.h"
    119 #include "taler-merchant-httpd_private-delete-donau-instance-ID.h"
    120 #endif
    121 
    122 /**
    123  * Backlog for listen operation on unix-domain sockets.
    124  */
    125 #define UNIX_BACKLOG 500
    126 
    127 /**
    128  * Default maximum upload size permitted.  Can be overridden
    129  * per handler.
    130  */
    131 #define DEFAULT_MAX_UPLOAD_SIZE (16 * 1024)
    132 
    133 char *TMH_currency;
    134 
    135 char *TMH_base_url;
    136 
    137 char *TMH_spa_dir;
    138 
    139 char *TMH_helper_email;
    140 
    141 char *TMH_helper_sms;
    142 
    143 char *TMH_allowed_payment_targets;
    144 
    145 char *TMH_default_persona;
    146 
    147 char *TMH_payment_target_regex;
    148 
    149 regex_t TMH_payment_target_re;
    150 
    151 int TMH_force_audit;
    152 
    153 struct TALER_MERCHANTDB_Plugin *TMH_db;
    154 
    155 struct GNUNET_CONTAINER_MultiHashMap *TMH_by_id_map;
    156 
    157 struct GNUNET_TIME_Relative TMH_default_pay_delay;
    158 
    159 struct GNUNET_TIME_Relative TMH_default_refund_delay;
    160 
    161 struct GNUNET_TIME_Relative TMH_default_wire_transfer_delay;
    162 
    163 enum GNUNET_TIME_RounderInterval TMH_default_wire_transfer_rounding_interval;
    164 
    165 int TMH_strict_v19;
    166 
    167 int TMH_auth_disabled;
    168 
    169 int TMH_have_self_provisioning;
    170 
    171 enum TEH_TanChannelSet TEH_mandatory_tan_channels;
    172 
    173 struct GNUNET_TIME_Relative TMH_legal_expiration;
    174 
    175 unsigned int TMH_num_cspecs;
    176 
    177 struct TALER_CurrencySpecification *TMH_cspecs;
    178 
    179 struct GNUNET_CURL_Context *TMH_curl_ctx;
    180 
    181 /**
    182  * Event handler for instance settings changes.
    183  */
    184 static struct GNUNET_DB_EventHandler *instance_eh;
    185 
    186 /**
    187  * True if we started any HTTP daemon.
    188  */
    189 static bool have_daemons;
    190 
    191 /**
    192  * Should a "Connection: close" header be added to each HTTP response?
    193  */
    194 static int merchant_connection_close;
    195 
    196 /**
    197  * Context for integrating #TMH_curl_ctx with the
    198  * GNUnet event loop.
    199  */
    200 static struct GNUNET_CURL_RescheduleContext *merchant_curl_rc;
    201 
    202 /**
    203  * Global return code
    204  */
    205 static int global_ret;
    206 
    207 /**
    208  * Our configuration.
    209  */
    210 static const struct GNUNET_CONFIGURATION_Handle *cfg;
    211 
    212 /**
    213  * Maximum length of a permissions string of a scope
    214  */
    215 #define TMH_MAX_SCOPE_PERMISSIONS_LEN 4096
    216 
    217 /**
    218  * Maximum length of a name of a scope
    219  */
    220 #define TMH_MAX_NAME_LEN 255
    221 
    222 /**
    223  * Represents a hard-coded set of default scopes with their
    224  * permissions and names
    225  */
    226 struct ScopePermissionMap
    227 {
    228   /**
    229    * The scope enum value
    230    */
    231   enum TMH_AuthScope as;
    232 
    233   /**
    234    * The scope name
    235    */
    236   char name[TMH_MAX_NAME_LEN];
    237 
    238   /**
    239    * The scope permissions string.
    240    * Comma-separated.
    241    */
    242   char permissions[TMH_MAX_SCOPE_PERMISSIONS_LEN];
    243 };
    244 
    245 /**
    246  * The default scopes array for merchant
    247  */
    248 struct ScopePermissionMap scope_permissions[] = {
    249   /* Deprecated since v19 */
    250   {
    251     .as = TMH_AS_ALL,
    252     .name = "write",
    253     .permissions = "*"
    254   },
    255   /* Full access for SPA */
    256   {
    257     .as = TMH_AS_ALL,
    258     .name = "all",
    259     .permissions = "*"
    260   },
    261   /* Full access for SPA */
    262   {
    263     .as = TMH_AS_SPA,
    264     .name = "spa",
    265     .permissions = "*"
    266   },
    267   /* Read-only access */
    268   {
    269     .as = TMH_AS_READ_ONLY,
    270     .name = "readonly",
    271     .permissions = "*-read"
    272   },
    273   /* Simple order management */
    274   {
    275     .as = TMH_AS_ORDER_SIMPLE,
    276     .name = "order-simple",
    277     .permissions = "orders-read,orders-write"
    278   },
    279   /* Simple order management for PoS, also allows inventory locking */
    280   {
    281     .as = TMH_AS_ORDER_POS,
    282     .name = "order-pos",
    283     .permissions = "orders-read,orders-write,inventory-lock"
    284   },
    285   /* Simple order management, also allows refunding */
    286   {
    287     .as = TMH_AS_ORDER_MGMT,
    288     .name = "order-mgmt",
    289     .permissions = "orders-read,orders-write,orders-refund"
    290   },
    291   /* Full order management, allows inventory locking and refunds */
    292   {
    293     .as = TMH_AS_ORDER_FULL,
    294     .name = "order-full",
    295     .permissions = "orders-read,orders-write,inventory-lock,orders-refund"
    296   },
    297   /* No permissions, dummy scope */
    298   {
    299     .as = TMH_AS_NONE,
    300   }
    301 };
    302 
    303 
    304 /**
    305  * Get permissions string for scope.
    306  * Also extracts the leftmost bit into the @a refreshable
    307  * output parameter.
    308  *
    309  * @param as the scope to get the permissions string from
    310  * @param[out] refreshable true if the token associated with this scope is refreshable.
    311  * @return the permissions string, or NULL if no such scope found
    312  */
    313 static const char*
    314 get_scope_permissions (enum TMH_AuthScope as,
    315                        bool *refreshable)
    316 {
    317   *refreshable = as & TMH_AS_REFRESHABLE;
    318   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    319   {
    320     /* We ignore the TMH_AS_REFRESHABLE bit */
    321     if ( (as & ~TMH_AS_REFRESHABLE)  ==
    322          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
    323       return scope_permissions[i].permissions;
    324   }
    325   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    326               "Failed to find required permissions for scope %d\n",
    327               as);
    328   return NULL;
    329 }
    330 
    331 
    332 /**
    333  * Checks if @a permission_required is in permissions of
    334  * @a scope.
    335  *
    336  * @param permission_required the permission to check.
    337  * @param scope the scope to check.
    338  * @return true if @a permission_required is in the permissions set of @a scope.
    339  */
    340 static bool
    341 permission_in_scope (const char *permission_required,
    342                      enum TMH_AuthScope scope)
    343 {
    344   char *permissions;
    345   const char *perms_tmp;
    346   bool is_read_perm = false;
    347   bool is_write_perm = false;
    348   bool refreshable;
    349   const char *last_dash;
    350 
    351   perms_tmp = get_scope_permissions (scope,
    352                                      &refreshable);
    353   if (NULL == perms_tmp)
    354   {
    355     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    356                 "Permission check failed: scope %d not understood\n",
    357                 (int) scope);
    358     return false;
    359   }
    360   last_dash = strrchr (permission_required,
    361                        '-');
    362   if (NULL != last_dash)
    363   {
    364     is_write_perm = (0 == strcmp (last_dash,
    365                                   "-write"));
    366     is_read_perm = (0 == strcmp (last_dash,
    367                                  "-read"));
    368   }
    369 
    370   if (0 == strcmp ("token-refresh",
    371                    permission_required))
    372   {
    373     if (! refreshable)
    374     {
    375       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    376                   "Permission check failed: token not refreshable\n");
    377     }
    378     return refreshable;
    379   }
    380   permissions = GNUNET_strdup (perms_tmp);
    381   {
    382     const char *perm = strtok (permissions,
    383                                ",");
    384 
    385     if (NULL == perm)
    386     {
    387       GNUNET_free (permissions);
    388       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    389                   "Permission check failed: empty permission set\n");
    390       return false;
    391     }
    392     while (NULL != perm)
    393     {
    394       if (0 == strcmp ("*",
    395                        perm))
    396       {
    397         GNUNET_free (permissions);
    398         return true;
    399       }
    400       if ( (0 == strcmp ("*-write",
    401                          perm)) &&
    402            (is_write_perm) )
    403       {
    404         GNUNET_free (permissions);
    405         return true;
    406       }
    407       if ( (0 == strcmp ("*-read",
    408                          perm)) &&
    409            (is_read_perm) )
    410       {
    411         GNUNET_free (permissions);
    412         return true;
    413       }
    414       if (0 == strcmp (permission_required,
    415                        perm))
    416       {
    417         GNUNET_free (permissions);
    418         return true;
    419       }
    420       perm = strtok (NULL,
    421                      ",");
    422     }
    423   }
    424   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    425               "Permission check failed: %s not found in %s\n",
    426               permission_required,
    427               permissions);
    428   GNUNET_free (permissions);
    429   return false;
    430 }
    431 
    432 
    433 bool
    434 TMH_scope_is_subset (enum TMH_AuthScope as,
    435                      enum TMH_AuthScope candidate)
    436 {
    437   const char *as_perms;
    438   const char *candidate_perms;
    439   char *permissions;
    440   bool as_refreshable;
    441   bool cand_refreshable;
    442 
    443   as_perms = get_scope_permissions (as,
    444                                     &as_refreshable);
    445   candidate_perms = get_scope_permissions (candidate,
    446                                            &cand_refreshable);
    447   if (! as_refreshable && cand_refreshable)
    448     return false;
    449   if ( (NULL == as_perms) &&
    450        (NULL != candidate_perms) )
    451     return false;
    452   if ( (NULL == candidate_perms) ||
    453        (0 == strcmp ("*",
    454                      as_perms)))
    455     return true;
    456   permissions = GNUNET_strdup (candidate_perms);
    457   {
    458     const char *perm;
    459 
    460     perm = strtok (permissions,
    461                    ",");
    462     if (NULL == perm)
    463     {
    464       GNUNET_free (permissions);
    465       return true;
    466     }
    467     while (NULL != perm)
    468     {
    469       if (! permission_in_scope (perm,
    470                                  as))
    471       {
    472         GNUNET_free (permissions);
    473         return false;
    474       }
    475       perm = strtok (NULL,
    476                      ",");
    477     }
    478   }
    479   GNUNET_free (permissions);
    480   return true;
    481 }
    482 
    483 
    484 enum TMH_AuthScope
    485 TMH_get_scope_by_name (const char *name)
    486 {
    487   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    488   {
    489     if (0 == strcasecmp (scope_permissions[i].name,
    490                          name))
    491       return scope_permissions[i].as;
    492   }
    493   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    494               "Name `%s' does not match any scope we understand\n",
    495               name);
    496   return TMH_AS_NONE;
    497 }
    498 
    499 
    500 const char*
    501 TMH_get_name_by_scope (enum TMH_AuthScope scope,
    502                        bool *refreshable)
    503 {
    504   *refreshable = scope & TMH_AS_REFRESHABLE;
    505   for (unsigned int i = 0; TMH_AS_NONE != scope_permissions[i].as; i++)
    506   {
    507     /* We ignore the TMH_AS_REFRESHABLE bit */
    508     if ( (scope & ~TMH_AS_REFRESHABLE)  ==
    509          (scope_permissions[i].as & ~TMH_AS_REFRESHABLE) )
    510       return scope_permissions[i].name;
    511   }
    512   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    513               "Scope #%d does not match any scope we understand\n",
    514               (int) scope);
    515   return NULL;
    516 }
    517 
    518 
    519 enum GNUNET_GenericReturnValue
    520 TMH_check_auth (const char *password,
    521                 struct TALER_MerchantAuthenticationSaltP *salt,
    522                 struct TALER_MerchantAuthenticationHashP *hash)
    523 {
    524   struct TALER_MerchantAuthenticationHashP val;
    525 
    526   if (GNUNET_is_zero (hash))
    527     return GNUNET_OK;
    528   if (NULL == password)
    529   {
    530     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    531                 "Denying access: empty password provided\n");
    532     return GNUNET_SYSERR;
    533   }
    534   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    535               "Checking against token with salt %s\n",
    536               TALER_B2S (salt));
    537   TALER_merchant_instance_auth_hash_with_salt (&val,
    538                                                salt,
    539                                                password);
    540   if (0 !=
    541       GNUNET_memcmp (&val,
    542                      hash))
    543   {
    544     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    545                 "Access denied: password does not match\n");
    546     return GNUNET_SYSERR;
    547   }
    548   return GNUNET_OK;
    549 }
    550 
    551 
    552 /**
    553  * Check if @a userpass grants access to @a instance.
    554  *
    555  * @param userpass base64 encoded "$USERNAME:$PASSWORD" value
    556  *        from HTTP Basic "Authentication" header
    557  * @param instance the access controlled instance
    558  */
    559 static enum GNUNET_GenericReturnValue
    560 check_auth_instance (const char *userpass,
    561                      struct TMH_MerchantInstance *instance)
    562 {
    563   char *tmp;
    564   char *colon;
    565   const char *instance_name;
    566   const char *password;
    567   const char *target_instance = "admin";
    568   enum GNUNET_GenericReturnValue ret;
    569 
    570   /* implicitly a zeroed out hash means no authentication */
    571   if (GNUNET_is_zero (&instance->auth.auth_hash))
    572     return GNUNET_OK;
    573   if (NULL == userpass)
    574   {
    575     GNUNET_break_op (0);
    576     return GNUNET_SYSERR;
    577   }
    578   if (0 ==
    579       GNUNET_STRINGS_base64_decode (userpass,
    580                                     strlen (userpass),
    581                                     (void**) &tmp))
    582   {
    583     GNUNET_break_op (0);
    584     return GNUNET_SYSERR;
    585   }
    586   colon = strchr (tmp,
    587                   ':');
    588   if (NULL == colon)
    589   {
    590     GNUNET_break_op (0);
    591     GNUNET_free (tmp);
    592     return GNUNET_SYSERR;
    593   }
    594   *colon = '\0';
    595   instance_name = tmp;
    596   password = colon + 1;
    597   /* instance->settings.id can be NULL if there is no instance yet */
    598   if (NULL != instance->settings.id)
    599     target_instance = instance->settings.id;
    600   if (0 != strcmp (instance_name,
    601                    target_instance))
    602   {
    603     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    604                 "Somebody tried to login to instance %s with username %s (login failed).\n",
    605                 target_instance,
    606                 instance_name);
    607     GNUNET_free (tmp);
    608     return GNUNET_SYSERR;
    609   }
    610   ret = TMH_check_auth (password,
    611                         &instance->auth.auth_salt,
    612                         &instance->auth.auth_hash);
    613   GNUNET_free (tmp);
    614   if (GNUNET_OK != ret)
    615   {
    616     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    617                 "Password provided does not match credentials for %s\n",
    618                 target_instance);
    619   }
    620   return ret;
    621 }
    622 
    623 
    624 void
    625 TMH_compute_auth (const char *token,
    626                   struct TALER_MerchantAuthenticationSaltP *salt,
    627                   struct TALER_MerchantAuthenticationHashP *hash)
    628 {
    629   GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE,
    630                               salt,
    631                               sizeof (*salt));
    632   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    633               "Computing initial auth using token with salt %s\n",
    634               TALER_B2S (salt));
    635   TALER_merchant_instance_auth_hash_with_salt (hash,
    636                                                salt,
    637                                                token);
    638 }
    639 
    640 
    641 void
    642 TMH_wire_method_free (struct TMH_WireMethod *wm)
    643 {
    644   GNUNET_free (wm->payto_uri.full_payto);
    645   GNUNET_free (wm->wire_method);
    646   GNUNET_free (wm->credit_facade_url);
    647   json_decref (wm->credit_facade_credentials);
    648   GNUNET_free (wm);
    649 }
    650 
    651 
    652 void
    653 TMH_instance_decref (struct TMH_MerchantInstance *mi)
    654 {
    655   struct TMH_WireMethod *wm;
    656 
    657   mi->rc--;
    658   if (0 != mi->rc)
    659     return;
    660   TMH_force_get_orders_resume (mi);
    661   while (NULL != (wm = (mi->wm_head)))
    662   {
    663     GNUNET_CONTAINER_DLL_remove (mi->wm_head,
    664                                  mi->wm_tail,
    665                                  wm);
    666     TMH_wire_method_free (wm);
    667   }
    668 
    669   GNUNET_free (mi->settings.id);
    670   GNUNET_free (mi->settings.name);
    671   GNUNET_free (mi->settings.email);
    672   GNUNET_free (mi->settings.phone);
    673   GNUNET_free (mi->settings.website);
    674   GNUNET_free (mi->settings.logo);
    675   json_decref (mi->settings.address);
    676   json_decref (mi->settings.jurisdiction);
    677   GNUNET_free (mi);
    678 }
    679 
    680 
    681 enum GNUNET_GenericReturnValue
    682 TMH_instance_free_cb (void *cls,
    683                       const struct GNUNET_HashCode *key,
    684                       void *value)
    685 {
    686   struct TMH_MerchantInstance *mi = value;
    687 
    688   (void) cls;
    689   (void) key;
    690   GNUNET_assert (GNUNET_OK ==
    691                  GNUNET_CONTAINER_multihashmap_remove (TMH_by_id_map,
    692                                                        &mi->h_instance,
    693                                                        mi));
    694   TMH_instance_decref (mi);
    695   return GNUNET_YES;
    696 }
    697 
    698 
    699 /**
    700  * Shutdown task (invoked when the application is being
    701  * terminated for any reason)
    702  *
    703  * @param cls NULL
    704  */
    705 static void
    706 do_shutdown (void *cls)
    707 {
    708   (void) cls;
    709   TALER_MHD_daemons_halt ();
    710   TMH_force_orders_resume ();
    711   TMH_force_ac_resume ();
    712   TMH_force_pc_resume ();
    713   TMH_force_kyc_resume ();
    714   TMH_force_gorc_resume ();
    715   TMH_force_wallet_get_order_resume ();
    716   TMH_force_wallet_refund_order_resume ();
    717   TMH_challenge_done ();
    718   TALER_MHD_daemons_destroy ();
    719   if (NULL != instance_eh)
    720   {
    721     TMH_db->event_listen_cancel (instance_eh);
    722     instance_eh = NULL;
    723   }
    724   TMH_EXCHANGES_done ();
    725   if (NULL != TMH_db)
    726   {
    727     TALER_MERCHANTDB_plugin_unload (TMH_db);
    728     TMH_db = NULL;
    729   }
    730   if (NULL != TMH_by_id_map)
    731   {
    732     GNUNET_CONTAINER_multihashmap_iterate (TMH_by_id_map,
    733                                            &TMH_instance_free_cb,
    734                                            NULL);
    735     GNUNET_CONTAINER_multihashmap_destroy (TMH_by_id_map);
    736     TMH_by_id_map = NULL;
    737   }
    738   TALER_TEMPLATING_done ();
    739   if (NULL != TMH_curl_ctx)
    740   {
    741     GNUNET_CURL_fini (TMH_curl_ctx);
    742     TMH_curl_ctx = NULL;
    743   }
    744   if (NULL != merchant_curl_rc)
    745   {
    746     GNUNET_CURL_gnunet_rc_destroy (merchant_curl_rc);
    747     merchant_curl_rc = NULL;
    748   }
    749   if (NULL != TMH_payment_target_regex)
    750   {
    751     regfree (&TMH_payment_target_re);
    752     GNUNET_free (TMH_payment_target_regex);
    753   }
    754 }
    755 
    756 
    757 /**
    758  * Function called whenever MHD is done with a request.  If the
    759  * request was a POST, we may have stored a `struct Buffer *` in the
    760  * @a con_cls that might still need to be cleaned up.  Call the
    761  * respective function to free the memory.
    762  *
    763  * @param cls client-defined closure
    764  * @param connection connection handle
    765  * @param con_cls value as set by the last call to
    766  *        the #MHD_AccessHandlerCallback
    767  * @param toe reason for request termination
    768  * @see #MHD_OPTION_NOTIFY_COMPLETED
    769  * @ingroup request
    770  */
    771 static void
    772 handle_mhd_completion_callback (void *cls,
    773                                 struct MHD_Connection *connection,
    774                                 void **con_cls,
    775                                 enum MHD_RequestTerminationCode toe)
    776 {
    777   struct TMH_HandlerContext *hc = *con_cls;
    778 
    779   (void) cls;
    780   if (NULL == hc)
    781     return;
    782   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
    783   {
    784 #if MHD_VERSION >= 0x00097304
    785     const union MHD_ConnectionInfo *ci;
    786     unsigned int http_status = 0;
    787 
    788     ci = MHD_get_connection_info (connection,
    789                                   MHD_CONNECTION_INFO_HTTP_STATUS);
    790     if (NULL != ci)
    791       http_status = ci->http_status;
    792     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    793                 "Request for `%s' completed with HTTP status %u (%d)\n",
    794                 hc->url,
    795                 http_status,
    796                 toe);
    797 #else
    798     (void) connection;
    799     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    800                 "Finished handling request for `%s' with MHD termination code %d\n",
    801                 hc->url,
    802                 (int) toe);
    803 #endif
    804   }
    805   if (NULL != hc->cc)
    806     hc->cc (hc->ctx);
    807   TALER_MHD_parse_post_cleanup_callback (hc->json_parse_context);
    808   GNUNET_free (hc->infix);
    809   if (NULL != hc->request_body)
    810     json_decref (hc->request_body);
    811   if (NULL != hc->instance)
    812     TMH_instance_decref (hc->instance);
    813   TMH_db->preflight (TMH_db->cls);
    814   GNUNET_free (hc->full_url);
    815   GNUNET_free (hc);
    816   *con_cls = NULL;
    817 }
    818 
    819 
    820 struct TMH_MerchantInstance *
    821 TMH_lookup_instance (const char *instance_id)
    822 {
    823   struct GNUNET_HashCode h_instance;
    824 
    825   if (NULL == instance_id)
    826     instance_id = "admin";
    827   GNUNET_CRYPTO_hash (instance_id,
    828                       strlen (instance_id),
    829                       &h_instance);
    830   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    831               "Looking for by-id key %s of '%s' in hashmap\n",
    832               GNUNET_h2s (&h_instance),
    833               instance_id);
    834   /* We're fine if that returns NULL, the calling routine knows how
    835      to handle that */
    836   return GNUNET_CONTAINER_multihashmap_get (TMH_by_id_map,
    837                                             &h_instance);
    838 }
    839 
    840 
    841 /**
    842  * Add instance definition to our active set of instances.
    843  *
    844  * @param[in,out] mi merchant instance details to define
    845  * @return #GNUNET_OK on success, #GNUNET_NO if the same ID is in use already
    846  */
    847 enum GNUNET_GenericReturnValue
    848 TMH_add_instance (struct TMH_MerchantInstance *mi)
    849 {
    850   const char *id;
    851   int ret;
    852 
    853   id = mi->settings.id;
    854   if (NULL == id)
    855     id = "admin";
    856   GNUNET_CRYPTO_hash (id,
    857                       strlen (id),
    858                       &mi->h_instance);
    859   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
    860               "Looking for by-id key %s of `%s' in hashmap\n",
    861               GNUNET_h2s (&mi->h_instance),
    862               id);
    863   ret = GNUNET_CONTAINER_multihashmap_put (TMH_by_id_map,
    864                                            &mi->h_instance,
    865                                            mi,
    866                                            GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY);
    867   if (GNUNET_OK == ret)
    868   {
    869     GNUNET_assert (mi->rc < UINT_MAX);
    870     mi->rc++;
    871   }
    872   return ret;
    873 }
    874 
    875 
    876 /**
    877  * Handle a OPTIONS "*" request.
    878  *
    879  * @param rh context of the handler
    880  * @param connection the MHD connection to handle
    881  * @param[in,out] hc context with further information about the request
    882  * @return MHD result code
    883  */
    884 static MHD_RESULT
    885 handle_server_options (const struct TMH_RequestHandler *rh,
    886                        struct MHD_Connection *connection,
    887                        struct TMH_HandlerContext *hc)
    888 {
    889   (void) rh;
    890   (void) hc;
    891   return TALER_MHD_reply_cors_preflight (connection);
    892 }
    893 
    894 
    895 /**
    896  * Generates the response for "/", redirecting the
    897  * client to the "/webui/" from where we serve the SPA.
    898  *
    899  * @param rh request handler
    900  * @param connection MHD connection
    901  * @param hc handler context
    902  * @return MHD result code
    903  */
    904 static MHD_RESULT
    905 spa_redirect (const struct TMH_RequestHandler *rh,
    906               struct MHD_Connection *connection,
    907               struct TMH_HandlerContext *hc)
    908 {
    909   const char *text = "Redirecting to /webui/";
    910   struct MHD_Response *response;
    911   char *dst;
    912 
    913   response = MHD_create_response_from_buffer (strlen (text),
    914                                               (void *) text,
    915                                               MHD_RESPMEM_PERSISTENT);
    916   if (NULL == response)
    917   {
    918     GNUNET_break (0);
    919     return MHD_NO;
    920   }
    921   TALER_MHD_add_global_headers (response,
    922                                 true);
    923   GNUNET_break (MHD_YES ==
    924                 MHD_add_response_header (response,
    925                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    926                                          "text/plain"));
    927   if ( (NULL == hc->instance) ||
    928        (0 == strcmp ("admin",
    929                      hc->instance->settings.id)) )
    930     dst = GNUNET_strdup ("/webui/");
    931   else
    932     GNUNET_asprintf (&dst,
    933                      "/instances/%s/webui/",
    934                      hc->instance->settings.id);
    935   if (MHD_NO ==
    936       MHD_add_response_header (response,
    937                                MHD_HTTP_HEADER_LOCATION,
    938                                dst))
    939   {
    940     GNUNET_break (0);
    941     MHD_destroy_response (response);
    942     GNUNET_free (dst);
    943     return MHD_NO;
    944   }
    945   GNUNET_free (dst);
    946 
    947   {
    948     MHD_RESULT ret;
    949 
    950     ret = MHD_queue_response (connection,
    951                               MHD_HTTP_FOUND,
    952                               response);
    953     MHD_destroy_response (response);
    954     return ret;
    955   }
    956 }
    957 
    958 
    959 /**
    960  * Extract the token from authorization header value @a auth.
    961  * The @a auth value can be a bearer token or a Basic
    962  * authentication header. In both cases, this function
    963  * updates @a auth to point to the actual credential,
    964  * skipping spaces.
    965  *
    966  * NOTE: We probably want to replace this function with MHD2
    967  * API calls in the future that are more robust.
    968  *
    969  * @param[in,out] auth pointer to authorization header value,
    970  *        will be updated to point to the start of the token
    971  *        or set to NULL if header value is invalid
    972  * @param[out] is_basic_auth will be set to true if the
    973  *        authorization header uses basic authentication,
    974  *        otherwise to false
    975  */
    976 static void
    977 extract_auth (const char **auth,
    978               bool *is_basic_auth)
    979 {
    980   const char *bearer = "Bearer ";
    981   const char *basic = "Basic ";
    982   const char *tok = *auth;
    983   size_t offset = 0;
    984   bool is_bearer = false;
    985 
    986   *is_basic_auth = false;
    987   if (0 == strncmp (tok,
    988                     bearer,
    989                     strlen (bearer)))
    990   {
    991     offset = strlen (bearer);
    992     is_bearer = true;
    993   }
    994   else if (0 == strncmp (tok,
    995                          basic,
    996                          strlen (basic)))
    997   {
    998     offset = strlen (basic);
    999     *is_basic_auth = true;
   1000   }
   1001   else
   1002   {
   1003     *auth = NULL;
   1004     return;
   1005   }
   1006   tok += offset;
   1007   while (' ' == *tok)
   1008     tok++;
   1009   if ( (is_bearer) &&
   1010        (0 != strncasecmp (tok,
   1011                           RFC_8959_PREFIX,
   1012                           strlen (RFC_8959_PREFIX))) )
   1013   {
   1014     *auth = NULL;
   1015     return;
   1016   }
   1017   *auth = tok;
   1018 }
   1019 
   1020 
   1021 /**
   1022  * Checks if the @a rh matches the given (parsed) URL.
   1023  *
   1024  * @param rh handler to compare against
   1025  * @param url the main URL (without "/private/" prefix, if any)
   1026  * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config'
   1027  * @param infix_url infix text, i.e. "$ORDER_ID".
   1028  * @param infix_strlen length of the string in @a infix_url
   1029  * @param suffix_url suffix, i.e. "/refund", including the "/"
   1030  * @param suffix_strlen number of characters in @a suffix_url
   1031  * @return true if @a rh matches this request
   1032  */
   1033 static bool
   1034 prefix_match (const struct TMH_RequestHandler *rh,
   1035               const char *url,
   1036               size_t prefix_strlen,
   1037               const char *infix_url,
   1038               size_t infix_strlen,
   1039               const char *suffix_url,
   1040               size_t suffix_strlen)
   1041 {
   1042   if ( (prefix_strlen != strlen (rh->url_prefix)) ||
   1043        (0 != memcmp (url,
   1044                      rh->url_prefix,
   1045                      prefix_strlen)) )
   1046     return false;
   1047   if (! rh->have_id_segment)
   1048   {
   1049     /* Require /$PREFIX/$SUFFIX or /$PREFIX */
   1050     if (NULL != suffix_url)
   1051       return false;       /* too many segments to match */
   1052     if ( (NULL == infix_url)   /* either or */
   1053          ^ (NULL == rh->url_suffix) )
   1054       return false;       /* suffix existence mismatch */
   1055     /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */
   1056     if ( (NULL != infix_url) &&
   1057          ( (infix_strlen != strlen (rh->url_suffix)) ||
   1058            (0 != memcmp (infix_url,
   1059                          rh->url_suffix,
   1060                          infix_strlen)) ) )
   1061       return false;       /* cannot use infix as suffix: content mismatch */
   1062   }
   1063   else
   1064   {
   1065     /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */
   1066     if (NULL == infix_url)
   1067       return false;       /* infix existence mismatch */
   1068     if ( ( (NULL == suffix_url)
   1069            ^ (NULL == rh->url_suffix) ) )
   1070       return false;       /* suffix existence mismatch */
   1071     if ( (NULL != suffix_url) &&
   1072          ( (suffix_strlen != strlen (rh->url_suffix)) ||
   1073            (0 != memcmp (suffix_url,
   1074                          rh->url_suffix,
   1075                          suffix_strlen)) ) )
   1076       return false;       /* suffix content mismatch */
   1077   }
   1078   return true;
   1079 }
   1080 
   1081 
   1082 /**
   1083  * Function called first by MHD with the full URL.
   1084  *
   1085  * @param cls NULL
   1086  * @param full_url the full URL
   1087  * @param con MHD connection object
   1088  * @return our handler context
   1089  */
   1090 static void *
   1091 full_url_track_callback (void *cls,
   1092                          const char *full_url,
   1093                          struct MHD_Connection *con)
   1094 {
   1095   struct TMH_HandlerContext *hc;
   1096 
   1097   hc = GNUNET_new (struct TMH_HandlerContext);
   1098   hc->connection = con;
   1099   GNUNET_async_scope_fresh (&hc->async_scope_id);
   1100   GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
   1101   hc->full_url = GNUNET_strdup (full_url);
   1102   return hc;
   1103 }
   1104 
   1105 
   1106 /**
   1107  * Function used to process Basic authorization header value.
   1108  * Sets correct scope in the auth_scope parameter of the
   1109  * #TMH_HandlerContext.
   1110  *
   1111  * @param hc the handler context
   1112  * @param authn_s the value of the authorization header
   1113  */
   1114 static void
   1115 process_basic_auth (struct TMH_HandlerContext *hc,
   1116                     const char *authn_s)
   1117 {
   1118   /* Handle token endpoint slightly differently: Only allow
   1119    * instance password (Basic auth) to retrieve access token.
   1120    * We need to handle authorization with Basic auth here first
   1121    * The only time we need to handle authentication like this is
   1122    * for the token endpoint!
   1123    */
   1124   if ( (0 != strncmp (hc->rh->url_prefix,
   1125                       "/token",
   1126                       strlen ("/token"))) ||
   1127        (0 != strncmp (MHD_HTTP_METHOD_POST,
   1128                       hc->rh->method,
   1129                       strlen (MHD_HTTP_METHOD_POST))) ||
   1130        (NULL == hc->instance))
   1131   {
   1132     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1133                 "Called endpoint `%s' with Basic authentication. Rejecting...\n",
   1134                 hc->rh->url_prefix);
   1135     hc->auth_scope = TMH_AS_NONE;
   1136     return;
   1137   }
   1138   if (GNUNET_OK ==
   1139       check_auth_instance (authn_s,
   1140                            hc->instance))
   1141   {
   1142     hc->auth_scope = TMH_AS_ALL;
   1143   }
   1144   else
   1145   {
   1146     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1147                 "Basic authentication failed!\n");
   1148     hc->auth_scope = TMH_AS_NONE;
   1149   }
   1150 }
   1151 
   1152 
   1153 /**
   1154  * Function used to process Bearer authorization header value.
   1155  * Sets correct scope in the auth_scope parameter of the
   1156  * #TMH_HandlerContext..
   1157  *
   1158  * @param hc the handler context
   1159  * @param authn_s the value of the authorization header
   1160  * @return TALER_EC_NONE on success.
   1161  */
   1162 static enum TALER_ErrorCode
   1163 process_bearer_auth (struct TMH_HandlerContext *hc,
   1164                      const char *authn_s)
   1165 {
   1166   if (NULL == hc->instance)
   1167   {
   1168     hc->auth_scope = TMH_AS_NONE;
   1169     return TALER_EC_NONE;
   1170   }
   1171   if (GNUNET_is_zero (&hc->instance->auth.auth_hash))
   1172   {
   1173     /* hash zero means no authentication for instance */
   1174     hc->auth_scope = TMH_AS_ALL;
   1175     return TALER_EC_NONE;
   1176   }
   1177   {
   1178     enum TALER_ErrorCode ec;
   1179 
   1180     ec = TMH_check_token (authn_s,
   1181                           hc->instance->settings.id,
   1182                           &hc->auth_scope);
   1183     if (TALER_EC_NONE != ec)
   1184     {
   1185       char *dec;
   1186       size_t dec_len;
   1187       const char *token;
   1188 
   1189       /* NOTE: Deprecated, remove sometime after v1.1 */
   1190       if (0 != strncasecmp (authn_s,
   1191                             RFC_8959_PREFIX,
   1192                             strlen (RFC_8959_PREFIX)))
   1193       {
   1194         GNUNET_break_op (0);
   1195         hc->auth_scope = TMH_AS_NONE;
   1196         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1197                     "Authentication token invalid: %d\n",
   1198                     (int) ec);
   1199         return ec;
   1200       }
   1201       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1202                   "Trying deprecated secret-token:password API authN\n");
   1203       token = authn_s + strlen (RFC_8959_PREFIX);
   1204       dec_len = GNUNET_STRINGS_urldecode (token,
   1205                                           strlen (token),
   1206                                           &dec);
   1207       if ( (0 == dec_len) ||
   1208            (GNUNET_OK !=
   1209             TMH_check_auth (dec,
   1210                             &hc->instance->auth.auth_salt,
   1211                             &hc->instance->auth.auth_hash)) )
   1212       {
   1213         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1214                     "Login failed\n");
   1215         hc->auth_scope = TMH_AS_NONE;
   1216         GNUNET_free (dec);
   1217         return TALER_EC_NONE;
   1218       }
   1219       hc->auth_scope = TMH_AS_ALL;
   1220       GNUNET_free (dec);
   1221     }
   1222   }
   1223   return TALER_EC_NONE;
   1224 }
   1225 
   1226 
   1227 /**
   1228  * A client has requested the given url using the given method
   1229  * (#MHD_HTTP_METHOD_GET, #MHD_HTTP_METHOD_PUT,
   1230  * #MHD_HTTP_METHOD_DELETE, #MHD_HTTP_METHOD_POST, etc).  The callback
   1231  * must call MHD callbacks to provide content to give back to the
   1232  * client and return an HTTP status code (i.e. #MHD_HTTP_OK,
   1233  * #MHD_HTTP_NOT_FOUND, etc.).
   1234  *
   1235  * @param cls argument given together with the function
   1236  *        pointer when the handler was registered with MHD
   1237  * @param connection the MHD connection to handle
   1238  * @param url the requested url
   1239  * @param method the HTTP method used (#MHD_HTTP_METHOD_GET,
   1240  *        #MHD_HTTP_METHOD_PUT, etc.)
   1241  * @param version the HTTP version string (i.e.
   1242  *        #MHD_HTTP_VERSION_1_1)
   1243  * @param upload_data the data being uploaded (excluding HEADERS,
   1244  *        for a POST that fits into memory and that is encoded
   1245  *        with a supported encoding, the POST data will NOT be
   1246  *        given in upload_data and is instead available as
   1247  *        part of #MHD_get_connection_values; very large POST
   1248  *        data *will* be made available incrementally in
   1249  *        @a upload_data)
   1250  * @param upload_data_size set initially to the size of the
   1251  *        @a upload_data provided; the method must update this
   1252  *        value to the number of bytes NOT processed;
   1253  * @param con_cls pointer that the callback can set to some
   1254  *        address and that will be preserved by MHD for future
   1255  *        calls for this request; since the access handler may
   1256  *        be called many times (i.e., for a PUT/POST operation
   1257  *        with plenty of upload data) this allows the application
   1258  *        to easily associate some request-specific state.
   1259  *        If necessary, this state can be cleaned up in the
   1260  *        global #MHD_RequestCompletedCallback (which
   1261  *        can be set with the #MHD_OPTION_NOTIFY_COMPLETED).
   1262  *        Initially, `*con_cls` will be set up by the
   1263  *        full_url_track_callback().
   1264  * @return #MHD_YES if the connection was handled successfully,
   1265  *         #MHD_NO if the socket must be closed due to a serious
   1266  *         error while handling the request
   1267  */
   1268 static MHD_RESULT
   1269 url_handler (void *cls,
   1270              struct MHD_Connection *connection,
   1271              const char *url,
   1272              const char *method,
   1273              const char *version,
   1274              const char *upload_data,
   1275              size_t *upload_data_size,
   1276              void **con_cls)
   1277 {
   1278   static struct TMH_RequestHandler management_handlers[] = {
   1279     /* GET /instances */
   1280     {
   1281       .url_prefix = "/instances",
   1282       .method = MHD_HTTP_METHOD_GET,
   1283       .permission = "instances-write",
   1284       .skip_instance = true,
   1285       .default_only = true,
   1286       .handler = &TMH_private_get_instances
   1287     },
   1288     /* POST /instances */
   1289     {
   1290       .url_prefix = "/instances",
   1291       .method = MHD_HTTP_METHOD_POST,
   1292       .permission = "instances-write",
   1293       .skip_instance = true,
   1294       .default_only = true,
   1295       .handler = &TMH_private_post_instances,
   1296       /* allow instance data of up to 8 MB, that should be plenty;
   1297          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1298          would require further changes to the allocation logic
   1299          in the code... */
   1300       .max_upload = 1024 * 1024 * 8
   1301     },
   1302     /* GET /instances/$ID/ */
   1303     {
   1304       .url_prefix = "/instances/",
   1305       .method = MHD_HTTP_METHOD_GET,
   1306       .permission = "instances-write",
   1307       .skip_instance = true,
   1308       .default_only = true,
   1309       .have_id_segment = true,
   1310       .handler = &TMH_private_get_instances_default_ID
   1311     },
   1312     /* DELETE /instances/$ID */
   1313     {
   1314       .url_prefix = "/instances/",
   1315       .method = MHD_HTTP_METHOD_DELETE,
   1316       .permission = "instances-write",
   1317       .skip_instance = true,
   1318       .default_only = true,
   1319       .have_id_segment = true,
   1320       .handler = &TMH_private_delete_instances_default_ID
   1321     },
   1322     /* PATCH /instances/$ID */
   1323     {
   1324       .url_prefix = "/instances/",
   1325       .method = MHD_HTTP_METHOD_PATCH,
   1326       .permission = "instances-write",
   1327       .skip_instance = true,
   1328       .default_only = true,
   1329       .have_id_segment = true,
   1330       .handler = &TMH_private_patch_instances_default_ID,
   1331       /* allow instance data of up to 8 MB, that should be plenty;
   1332          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1333          would require further changes to the allocation logic
   1334          in the code... */
   1335       .max_upload = 1024 * 1024 * 8
   1336     },
   1337     /* POST /auth: */
   1338     {
   1339       .url_prefix = "/instances/",
   1340       .url_suffix = "auth",
   1341       .method = MHD_HTTP_METHOD_POST,
   1342       .permission = "instances-auth-write",
   1343       .skip_instance = true,
   1344       .default_only = true,
   1345       .have_id_segment = true,
   1346       .handler = &TMH_private_post_instances_default_ID_auth,
   1347       /* Body should be pretty small. */
   1348       .max_upload = 1024 * 1024
   1349     },
   1350     /* GET /kyc: */
   1351     {
   1352       .url_prefix = "/instances/",
   1353       .url_suffix = "kyc",
   1354       .method = MHD_HTTP_METHOD_GET,
   1355       .permission = "instances-kyc-read",
   1356       .skip_instance = true,
   1357       .default_only = true,
   1358       .have_id_segment = true,
   1359       .handler = &TMH_private_get_instances_default_ID_kyc,
   1360     },
   1361     {
   1362       .url_prefix = NULL
   1363     }
   1364   };
   1365 
   1366   static struct TMH_RequestHandler private_handlers[] = {
   1367     /* GET /instances/$ID/: */
   1368     {
   1369       .url_prefix = "/",
   1370       .method = MHD_HTTP_METHOD_GET,
   1371       .permission = "instances-read",
   1372       .handler = &TMH_private_get_instances_ID
   1373     },
   1374     /* DELETE /instances/$ID/: */
   1375     {
   1376       .url_prefix = "/",
   1377       .method = MHD_HTTP_METHOD_DELETE,
   1378       .permission = "instances-write",
   1379       .allow_deleted_instance = true,
   1380       .handler = &TMH_private_delete_instances_ID
   1381     },
   1382     /* PATCH /instances/$ID/: */
   1383     {
   1384       .url_prefix = "/",
   1385       .method = MHD_HTTP_METHOD_PATCH,
   1386       .handler = &TMH_private_patch_instances_ID,
   1387       .permission = "instances-write",
   1388       .allow_deleted_instance = true,
   1389       /* allow instance data of up to 8 MB, that should be plenty;
   1390          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1391          would require further changes to the allocation logic
   1392          in the code... */
   1393       .max_upload = 1024 * 1024 * 8
   1394     },
   1395     /* POST /auth: */
   1396     {
   1397       .url_prefix = "/auth",
   1398       .method = MHD_HTTP_METHOD_POST,
   1399       .handler = &TMH_private_post_instances_ID_auth,
   1400       .permission = "auth-write",
   1401       /* Body should be pretty small. */
   1402       .max_upload = 1024 * 1024,
   1403     },
   1404     /* GET /kyc: */
   1405     {
   1406       .url_prefix = "/kyc",
   1407       .method = MHD_HTTP_METHOD_GET,
   1408       .permission = "kyc-read",
   1409       .handler = &TMH_private_get_instances_ID_kyc,
   1410     },
   1411     /* GET /pos: */
   1412     {
   1413       .url_prefix = "/pos",
   1414       .method = MHD_HTTP_METHOD_GET,
   1415       .permission = "pos-read",
   1416       .handler = &TMH_private_get_pos
   1417     },
   1418     /* GET /categories: */
   1419     {
   1420       .url_prefix = "/categories",
   1421       .method = MHD_HTTP_METHOD_GET,
   1422       .permission = "categories-read",
   1423       .handler = &TMH_private_get_categories
   1424     },
   1425     /* POST /categories: */
   1426     {
   1427       .url_prefix = "/categories",
   1428       .method = MHD_HTTP_METHOD_POST,
   1429       .permission = "categories-write",
   1430       .handler = &TMH_private_post_categories,
   1431       /* allow category data of up to 8 kb, that should be plenty */
   1432       .max_upload = 1024 * 8
   1433     },
   1434     /* GET /categories/$ID: */
   1435     {
   1436       .url_prefix = "/categories/",
   1437       .method = MHD_HTTP_METHOD_GET,
   1438       .permission = "categories-read",
   1439       .have_id_segment = true,
   1440       .allow_deleted_instance = true,
   1441       .handler = &TMH_private_get_categories_ID
   1442     },
   1443     /* DELETE /categories/$ID: */
   1444     {
   1445       .url_prefix = "/categories/",
   1446       .method = MHD_HTTP_METHOD_DELETE,
   1447       .permission = "categories-write",
   1448       .have_id_segment = true,
   1449       .allow_deleted_instance = true,
   1450       .handler = &TMH_private_delete_categories_ID
   1451     },
   1452     /* PATCH /categories/$ID/: */
   1453     {
   1454       .url_prefix = "/categories/",
   1455       .method = MHD_HTTP_METHOD_PATCH,
   1456       .permission = "categories-write",
   1457       .have_id_segment = true,
   1458       .allow_deleted_instance = true,
   1459       .handler = &TMH_private_patch_categories_ID,
   1460       /* allow category data of up to 8 kb, that should be plenty */
   1461       .max_upload = 1024 * 8
   1462     },
   1463     /* GET /units: */
   1464     {
   1465       .url_prefix = "/units",
   1466       .method = MHD_HTTP_METHOD_GET,
   1467       .handler = &TMH_private_get_units
   1468     },
   1469     /* POST /units: */
   1470     {
   1471       .url_prefix = "/units",
   1472       .method = MHD_HTTP_METHOD_POST,
   1473       .permission = "units-write",
   1474       .handler = &TMH_private_post_units,
   1475       .max_upload = 1024 * 8
   1476     },
   1477     /* GET /units/$UNIT: */
   1478     {
   1479       .url_prefix = "/units/",
   1480       .method = MHD_HTTP_METHOD_GET,
   1481       .have_id_segment = true,
   1482       .allow_deleted_instance = true,
   1483       .handler = &TMH_private_get_units_ID
   1484     },
   1485     /* DELETE /units/$UNIT: */
   1486     {
   1487       .url_prefix = "/units/",
   1488       .method = MHD_HTTP_METHOD_DELETE,
   1489       .permission = "units-write",
   1490       .have_id_segment = true,
   1491       .allow_deleted_instance = true,
   1492       .handler = &TMH_private_delete_units_ID
   1493     },
   1494     /* PATCH /units/$UNIT: */
   1495     {
   1496       .url_prefix = "/units/",
   1497       .method = MHD_HTTP_METHOD_PATCH,
   1498       .permission = "units-write",
   1499       .have_id_segment = true,
   1500       .allow_deleted_instance = true,
   1501       .handler = &TMH_private_patch_units_ID,
   1502       .max_upload = 1024 * 8
   1503     },
   1504     /* GET /products: */
   1505     {
   1506       .url_prefix = "/products",
   1507       .permission = "products-read",
   1508       .method = MHD_HTTP_METHOD_GET,
   1509       .handler = &TMH_private_get_products
   1510     },
   1511     /* POST /products: */
   1512     {
   1513       .url_prefix = "/products",
   1514       .method = MHD_HTTP_METHOD_POST,
   1515       .permission = "products-write",
   1516       .handler = &TMH_private_post_products,
   1517       /* allow product data of up to 8 MB, that should be plenty;
   1518          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1519          would require further changes to the allocation logic
   1520          in the code... */
   1521       .max_upload = 1024 * 1024 * 8
   1522     },
   1523     /* GET /products/$ID: */
   1524     {
   1525       .url_prefix = "/products/",
   1526       .method = MHD_HTTP_METHOD_GET,
   1527       .have_id_segment = true,
   1528       .permission = "products-read",
   1529       .allow_deleted_instance = true,
   1530       .handler = &TMH_private_get_products_ID
   1531     },
   1532     /* DELETE /products/$ID/: */
   1533     {
   1534       .url_prefix = "/products/",
   1535       .method = MHD_HTTP_METHOD_DELETE,
   1536       .have_id_segment = true,
   1537       .permission = "products-write",
   1538       .allow_deleted_instance = true,
   1539       .handler = &TMH_private_delete_products_ID
   1540     },
   1541     /* PATCH /products/$ID/: */
   1542     {
   1543       .url_prefix = "/products/",
   1544       .method = MHD_HTTP_METHOD_PATCH,
   1545       .have_id_segment = true,
   1546       .allow_deleted_instance = true,
   1547       .permission = "products-write",
   1548       .handler = &TMH_private_patch_products_ID,
   1549       /* allow product data of up to 8 MB, that should be plenty;
   1550          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1551          would require further changes to the allocation logic
   1552          in the code... */
   1553       .max_upload = 1024 * 1024 * 8
   1554     },
   1555     /* POST /products/$ID/lock: */
   1556     {
   1557       .url_prefix = "/products/",
   1558       .url_suffix = "lock",
   1559       .method = MHD_HTTP_METHOD_POST,
   1560       .have_id_segment = true,
   1561       .permission = "products-lock",
   1562       .handler = &TMH_private_post_products_ID_lock,
   1563       /* the body should be pretty small, allow 1 MB of upload
   1564          to set a conservative bound for sane wallets */
   1565       .max_upload = 1024 * 1024
   1566     },
   1567     /* POST /orders: */
   1568     {
   1569       .url_prefix = "/orders",
   1570       .method = MHD_HTTP_METHOD_POST,
   1571       .permission = "orders-write",
   1572       .handler = &TMH_private_post_orders,
   1573       /* allow contracts of up to 8 MB, that should be plenty;
   1574          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1575          would require further changes to the allocation logic
   1576          in the code... */
   1577       .max_upload = 1024 * 1024 * 8
   1578     },
   1579     /* GET /orders/$ID: */
   1580     {
   1581       .url_prefix = "/orders/",
   1582       .method = MHD_HTTP_METHOD_GET,
   1583       .permission = "orders-read",
   1584       .have_id_segment = true,
   1585       .allow_deleted_instance = true,
   1586       .handler = &TMH_private_get_orders_ID
   1587     },
   1588     /* GET /orders: */
   1589     {
   1590       .url_prefix = "/orders",
   1591       .method = MHD_HTTP_METHOD_GET,
   1592       .permission = "orders-read",
   1593       .allow_deleted_instance = true,
   1594       .handler = &TMH_private_get_orders
   1595     },
   1596     /* POST /orders/$ID/refund: */
   1597     {
   1598       .url_prefix = "/orders/",
   1599       .url_suffix = "refund",
   1600       .method = MHD_HTTP_METHOD_POST,
   1601       .have_id_segment = true,
   1602       .permission = "orders-refund",
   1603       .handler = &TMH_private_post_orders_ID_refund,
   1604       /* the body should be pretty small, allow 1 MB of upload
   1605          to set a conservative bound for sane wallets */
   1606       .max_upload = 1024 * 1024
   1607     },
   1608     /* PATCH /orders/$ID/forget: */
   1609     {
   1610       .url_prefix = "/orders/",
   1611       .url_suffix = "forget",
   1612       .method = MHD_HTTP_METHOD_PATCH,
   1613       .permission = "orders-write",
   1614       .have_id_segment = true,
   1615       .allow_deleted_instance = true,
   1616       .handler = &TMH_private_patch_orders_ID_forget,
   1617       /* the body should be pretty small, allow 1 MB of upload
   1618          to set a conservative bound for sane wallets */
   1619       .max_upload = 1024 * 1024
   1620     },
   1621     /* DELETE /orders/$ID: */
   1622     {
   1623       .url_prefix = "/orders/",
   1624       .method = MHD_HTTP_METHOD_DELETE,
   1625       .permission = "orders-write",
   1626       .have_id_segment = true,
   1627       .allow_deleted_instance = true,
   1628       .handler = &TMH_private_delete_orders_ID
   1629     },
   1630     /* POST /transfers: */
   1631     {
   1632       .url_prefix = "/transfers",
   1633       .method = MHD_HTTP_METHOD_POST,
   1634       .allow_deleted_instance = true,
   1635       .handler = &TMH_private_post_transfers,
   1636       .permission = "transfers-write",
   1637       /* the body should be pretty small, allow 1 MB of upload
   1638          to set a conservative bound for sane wallets */
   1639       .max_upload = 1024 * 1024
   1640     },
   1641     /* DELETE /transfers/$ID: */
   1642     {
   1643       .url_prefix = "/transfers/",
   1644       .method = MHD_HTTP_METHOD_DELETE,
   1645       .permission = "transfers-write",
   1646       .allow_deleted_instance = true,
   1647       .handler = &TMH_private_delete_transfers_ID,
   1648       .have_id_segment = true,
   1649       /* the body should be pretty small, allow 1 MB of upload
   1650          to set a conservative bound for sane wallets */
   1651       .max_upload = 1024 * 1024
   1652     },
   1653     /* GET /transfers: */
   1654     {
   1655       .url_prefix = "/transfers",
   1656       .permission = "transfers-read",
   1657       .method = MHD_HTTP_METHOD_GET,
   1658       .allow_deleted_instance = true,
   1659       .handler = &TMH_private_get_transfers
   1660     },
   1661     /* GET /incoming: */
   1662     {
   1663       .url_prefix = "/incoming",
   1664       .permission = "transfers-read",
   1665       .method = MHD_HTTP_METHOD_GET,
   1666       .allow_deleted_instance = true,
   1667       .handler = &TMH_private_get_incoming
   1668     },
   1669     /* POST /otp-devices: */
   1670     {
   1671       .url_prefix = "/otp-devices",
   1672       .permission = "otp-devices-write",
   1673       .method = MHD_HTTP_METHOD_POST,
   1674       .handler = &TMH_private_post_otp_devices
   1675     },
   1676     /* GET /otp-devices: */
   1677     {
   1678       .url_prefix = "/otp-devices",
   1679       .permission = "opt-devices-read",
   1680       .method = MHD_HTTP_METHOD_GET,
   1681       .handler = &TMH_private_get_otp_devices
   1682     },
   1683     /* GET /otp-devices/$ID/: */
   1684     {
   1685       .url_prefix = "/otp-devices/",
   1686       .method = MHD_HTTP_METHOD_GET,
   1687       .permission = "otp-devices-read",
   1688       .have_id_segment = true,
   1689       .handler = &TMH_private_get_otp_devices_ID
   1690     },
   1691     /* DELETE /otp-devices/$ID/: */
   1692     {
   1693       .url_prefix = "/otp-devices/",
   1694       .method = MHD_HTTP_METHOD_DELETE,
   1695       .permission = "otp-devices-write",
   1696       .have_id_segment = true,
   1697       .handler = &TMH_private_delete_otp_devices_ID
   1698     },
   1699     /* PATCH /otp-devices/$ID/: */
   1700     {
   1701       .url_prefix = "/otp-devices/",
   1702       .method = MHD_HTTP_METHOD_PATCH,
   1703       .permission = "otp-devices-write",
   1704       .have_id_segment = true,
   1705       .handler = &TMH_private_patch_otp_devices_ID
   1706     },
   1707     /* POST /templates: */
   1708     {
   1709       .url_prefix = "/templates",
   1710       .method = MHD_HTTP_METHOD_POST,
   1711       .permission = "templates-write",
   1712       .handler = &TMH_private_post_templates,
   1713       /* allow template data of up to 8 MB, that should be plenty;
   1714          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1715          would require further changes to the allocation logic
   1716          in the code... */
   1717       .max_upload = 1024 * 1024 * 8
   1718     },
   1719     /* GET /templates: */
   1720     {
   1721       .url_prefix = "/templates",
   1722       .permission = "templates-read",
   1723       .method = MHD_HTTP_METHOD_GET,
   1724       .handler = &TMH_private_get_templates
   1725     },
   1726     /* GET /templates/$ID/: */
   1727     {
   1728       .url_prefix = "/templates/",
   1729       .method = MHD_HTTP_METHOD_GET,
   1730       .permission = "templates-read",
   1731       .have_id_segment = true,
   1732       .allow_deleted_instance = true,
   1733       .handler = &TMH_private_get_templates_ID
   1734     },
   1735     /* DELETE /templates/$ID/: */
   1736     {
   1737       .url_prefix = "/templates/",
   1738       .method = MHD_HTTP_METHOD_DELETE,
   1739       .permission = "templates-write",
   1740       .have_id_segment = true,
   1741       .allow_deleted_instance = true,
   1742       .handler = &TMH_private_delete_templates_ID
   1743     },
   1744     /* PATCH /templates/$ID/: */
   1745     {
   1746       .url_prefix = "/templates/",
   1747       .method = MHD_HTTP_METHOD_PATCH,
   1748       .permission = "templates-write",
   1749       .have_id_segment = true,
   1750       .allow_deleted_instance = true,
   1751       .handler = &TMH_private_patch_templates_ID,
   1752       /* allow template data of up to 8 MB, that should be plenty;
   1753          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1754          would require further changes to the allocation logic
   1755          in the code... */
   1756       .max_upload = 1024 * 1024 * 8
   1757     },
   1758     /* GET /webhooks: */
   1759     {
   1760       .url_prefix = "/webhooks",
   1761       .permission = "webhooks-read",
   1762       .method = MHD_HTTP_METHOD_GET,
   1763       .handler = &TMH_private_get_webhooks
   1764     },
   1765     /* POST /webhooks: */
   1766     {
   1767       .url_prefix = "/webhooks",
   1768       .method = MHD_HTTP_METHOD_POST,
   1769       .permission = "webhooks-write",
   1770       .handler = &TMH_private_post_webhooks,
   1771       /* allow webhook data of up to 8 MB, that should be plenty;
   1772          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1773          would require further changes to the allocation logic
   1774          in the code... */
   1775       .max_upload = 1024 * 1024 * 8
   1776     },
   1777     /* GET /webhooks/$ID/: */
   1778     {
   1779       .url_prefix = "/webhooks/",
   1780       .method = MHD_HTTP_METHOD_GET,
   1781       .permission = "webhooks-read",
   1782       .have_id_segment = true,
   1783       .allow_deleted_instance = true,
   1784       .handler = &TMH_private_get_webhooks_ID
   1785     },
   1786     /* DELETE /webhooks/$ID/: */
   1787     {
   1788       .url_prefix = "/webhooks/",
   1789       .permission = "webhooks-write",
   1790       .method = MHD_HTTP_METHOD_DELETE,
   1791       .have_id_segment = true,
   1792       .allow_deleted_instance = true,
   1793       .handler = &TMH_private_delete_webhooks_ID
   1794     },
   1795     /* PATCH /webhooks/$ID/: */
   1796     {
   1797       .url_prefix = "/webhooks/",
   1798       .method = MHD_HTTP_METHOD_PATCH,
   1799       .permission = "webhooks-write",
   1800       .have_id_segment = true,
   1801       .allow_deleted_instance = true,
   1802       .handler = &TMH_private_patch_webhooks_ID,
   1803       /* allow webhook data of up to 8 MB, that should be plenty;
   1804          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1805          would require further changes to the allocation logic
   1806          in the code... */
   1807       .max_upload = 1024 * 1024 * 8
   1808     },
   1809     /* POST /accounts: */
   1810     {
   1811       .url_prefix = "/accounts",
   1812       .method = MHD_HTTP_METHOD_POST,
   1813       .permission = "accounts-write",
   1814       .handler = &TMH_private_post_account,
   1815       /* allow account details of up to 8 kb, that should be plenty */
   1816       .max_upload = 1024 * 8
   1817     },
   1818     /* PATCH /accounts/$H_WIRE: */
   1819     {
   1820       .url_prefix = "/accounts/",
   1821       .method = MHD_HTTP_METHOD_PATCH,
   1822       .permission = "accounts-write",
   1823       .handler = &TMH_private_patch_accounts_ID,
   1824       .have_id_segment = true,
   1825       /* allow account details of up to 8 kb, that should be plenty */
   1826       .max_upload = 1024 * 8
   1827     },
   1828     /* GET /accounts: */
   1829     {
   1830       .url_prefix = "/accounts",
   1831       .permission = "accounts-read",
   1832       .method = MHD_HTTP_METHOD_GET,
   1833       .handler = &TMH_private_get_accounts
   1834     },
   1835     /* GET /accounts/$H_WIRE: */
   1836     {
   1837       .url_prefix = "/accounts/",
   1838       .permission = "accounts-read",
   1839       .method = MHD_HTTP_METHOD_GET,
   1840       .have_id_segment = true,
   1841       .handler = &TMH_private_get_accounts_ID
   1842     },
   1843     /* DELETE /accounts/$H_WIRE: */
   1844     {
   1845       .url_prefix = "/accounts/",
   1846       .permission = "accounts-write",
   1847       .method = MHD_HTTP_METHOD_DELETE,
   1848       .handler = &TMH_private_delete_account_ID,
   1849       .have_id_segment = true
   1850     },
   1851     /* GET /tokens: */
   1852     {
   1853       .url_prefix = "/tokens",
   1854       .permission = "tokens-read",
   1855       .method = MHD_HTTP_METHOD_GET,
   1856       .handler = &TMH_private_get_instances_ID_tokens,
   1857     },
   1858     /* POST /token: */
   1859     {
   1860       .url_prefix = "/token",
   1861       .permission = "token-refresh",
   1862       .method = MHD_HTTP_METHOD_POST,
   1863       .handler = &TMH_private_post_instances_ID_token,
   1864       /* Body should be tiny. */
   1865       .max_upload = 1024
   1866     },
   1867     /* DELETE /tokens/$SERIAL: */
   1868     {
   1869       .url_prefix = "/tokens/",
   1870       .permission = "tokens-write",
   1871       .method = MHD_HTTP_METHOD_DELETE,
   1872       .handler = &TMH_private_delete_instances_ID_token_SERIAL,
   1873       .have_id_segment = true
   1874     },
   1875     /* DELETE /token: */
   1876     {
   1877       .url_prefix = "/token",
   1878       .method = MHD_HTTP_METHOD_DELETE,
   1879       .handler = &TMH_private_delete_instances_ID_token,
   1880     },
   1881     /* GET /tokenfamilies: */
   1882     {
   1883       .url_prefix = "/tokenfamilies",
   1884       .permission = "tokenfamilies-read",
   1885       .method = MHD_HTTP_METHOD_GET,
   1886       .handler = &TMH_private_get_tokenfamilies
   1887     },
   1888     /* POST /tokenfamilies: */
   1889     {
   1890       .url_prefix = "/tokenfamilies",
   1891       .permission = "tokenfamilies-write",
   1892       .method = MHD_HTTP_METHOD_POST,
   1893       .handler = &TMH_private_post_token_families
   1894     },
   1895     /* GET /tokenfamilies/$SLUG/: */
   1896     {
   1897       .url_prefix = "/tokenfamilies/",
   1898       .method = MHD_HTTP_METHOD_GET,
   1899       .permission = "tokenfamilies-read",
   1900       .have_id_segment = true,
   1901       .handler = &TMH_private_get_tokenfamilies_SLUG
   1902     },
   1903     /* DELETE /tokenfamilies/$SLUG/: */
   1904     {
   1905       .url_prefix = "/tokenfamilies/",
   1906       .method = MHD_HTTP_METHOD_DELETE,
   1907       .permission = "tokenfamilies-write",
   1908       .have_id_segment = true,
   1909       .handler = &TMH_private_delete_token_families_SLUG
   1910     },
   1911     /* PATCH /tokenfamilies/$SLUG/: */
   1912     {
   1913       .url_prefix = "/tokenfamilies/",
   1914       .method = MHD_HTTP_METHOD_PATCH,
   1915       .permission = "tokenfamilies-write",
   1916       .have_id_segment = true,
   1917       .handler = &TMH_private_patch_token_family_SLUG,
   1918     },
   1919     #ifdef HAVE_DONAU_DONAU_SERVICE_H
   1920     /* GET /donau */
   1921     {
   1922       .url_prefix = "/donau",
   1923       .method = MHD_HTTP_METHOD_GET,
   1924       .handler = &TMH_private_get_donau_instances
   1925     },
   1926     /* POST /donau */
   1927     {
   1928       .url_prefix = "/donau",
   1929       .method = MHD_HTTP_METHOD_POST,
   1930       .handler = &TMH_private_post_donau_instance
   1931     },
   1932     /* DELETE /donau/$charity-id */
   1933     {
   1934       .url_prefix = "/donau/",
   1935       .method = MHD_HTTP_METHOD_DELETE,
   1936       .have_id_segment = true,
   1937       .handler = &TMH_private_delete_donau_instance_ID
   1938     },
   1939     #endif
   1940     /* GET /statistics-counter/$SLUG: */
   1941     {
   1942       .url_prefix = "/statistics-counter/",
   1943       .method = MHD_HTTP_METHOD_GET,
   1944       .permission = "statistics-read",
   1945       .have_id_segment = true,
   1946       .handler = &TMH_private_get_statistics_counter_SLUG,
   1947     },
   1948     /* GET /statistics-amount/$SLUG: */
   1949     {
   1950       .url_prefix = "/statistics-amount/",
   1951       .method = MHD_HTTP_METHOD_GET,
   1952       .permission = "statistics-read",
   1953       .have_id_segment = true,
   1954       .handler = &TMH_private_get_statistics_amount_SLUG,
   1955     },
   1956     {
   1957       .url_prefix = NULL
   1958     }
   1959   };
   1960   static struct TMH_RequestHandler public_handlers[] = {
   1961     {
   1962       /* for "admin" instance, it does not even
   1963          have to exist before we give the WebUI */
   1964       .url_prefix = "/",
   1965       .method = MHD_HTTP_METHOD_GET,
   1966       .mime_type = "text/html",
   1967       .skip_instance = true,
   1968       .default_only = true,
   1969       .handler = &spa_redirect,
   1970       .response_code = MHD_HTTP_FOUND
   1971     },
   1972     {
   1973       .url_prefix = "/config",
   1974       .method = MHD_HTTP_METHOD_GET,
   1975       .skip_instance = true,
   1976       .default_only = true,
   1977       .handler = &MH_handler_config
   1978     },
   1979     {
   1980       /* for "normal" instance,s they must exist
   1981          before we give the WebUI */
   1982       .url_prefix = "/",
   1983       .method = MHD_HTTP_METHOD_GET,
   1984       .mime_type = "text/html",
   1985       .handler = &spa_redirect,
   1986       .response_code = MHD_HTTP_FOUND
   1987     },
   1988     {
   1989       .url_prefix = "/webui/",
   1990       .method = MHD_HTTP_METHOD_GET,
   1991       .mime_type = "text/html",
   1992       .skip_instance = true,
   1993       .have_id_segment = true,
   1994       .handler = &TMH_return_spa,
   1995       .response_code = MHD_HTTP_OK
   1996     },
   1997     {
   1998       .url_prefix = "/agpl",
   1999       .method = MHD_HTTP_METHOD_GET,
   2000       .skip_instance = true,
   2001       .handler = &TMH_MHD_handler_agpl_redirect
   2002     },
   2003     {
   2004       .url_prefix = "/agpl",
   2005       .method = MHD_HTTP_METHOD_GET,
   2006       .skip_instance = true,
   2007       .handler = &TMH_MHD_handler_agpl_redirect
   2008     },
   2009     {
   2010       .url_prefix = "/terms",
   2011       .method = MHD_HTTP_METHOD_GET,
   2012       .skip_instance = true,
   2013       .handler = &TMH_handler_terms
   2014     },
   2015     {
   2016       .url_prefix = "/privacy",
   2017       .method = MHD_HTTP_METHOD_GET,
   2018       .skip_instance = true,
   2019       .handler = &TMH_handler_privacy
   2020     },
   2021     /* Also serve the same /config per instance */
   2022     {
   2023       .url_prefix = "/config",
   2024       .method = MHD_HTTP_METHOD_GET,
   2025       .handler = &MH_handler_config
   2026     },
   2027     /* POST /orders/$ID/abort: */
   2028     {
   2029       .url_prefix = "/orders/",
   2030       .have_id_segment = true,
   2031       .url_suffix = "abort",
   2032       .method = MHD_HTTP_METHOD_POST,
   2033       .handler = &TMH_post_orders_ID_abort,
   2034       /* wallet may give us many coins to sign, allow 1 MB of upload
   2035          to set a conservative bound for sane wallets */
   2036       .max_upload = 1024 * 1024
   2037     },
   2038     /* POST /orders/$ID/claim: */
   2039     {
   2040       .url_prefix = "/orders/",
   2041       .have_id_segment = true,
   2042       .url_suffix = "claim",
   2043       .method = MHD_HTTP_METHOD_POST,
   2044       .handler = &TMH_post_orders_ID_claim,
   2045       /* the body should be pretty small, allow 1 MB of upload
   2046          to set a conservative bound for sane wallets */
   2047       .max_upload = 1024 * 1024
   2048     },
   2049     /* POST /orders/$ID/pay: */
   2050     {
   2051       .url_prefix = "/orders/",
   2052       .have_id_segment = true,
   2053       .url_suffix = "pay",
   2054       .method = MHD_HTTP_METHOD_POST,
   2055       .handler = &TMH_post_orders_ID_pay,
   2056       /* wallet may give us many coins to sign, allow 1 MB of upload
   2057          to set a conservative bound for sane wallets */
   2058       .max_upload = 1024 * 1024
   2059     },
   2060     /* POST /orders/$ID/paid: */
   2061     {
   2062       .url_prefix = "/orders/",
   2063       .have_id_segment = true,
   2064       .allow_deleted_instance = true,
   2065       .url_suffix = "paid",
   2066       .method = MHD_HTTP_METHOD_POST,
   2067       .handler = &TMH_post_orders_ID_paid,
   2068       /* the body should be pretty small, allow 1 MB of upload
   2069          to set a conservative bound for sane wallets */
   2070       .max_upload = 1024 * 1024
   2071     },
   2072     /* POST /orders/$ID/refund: */
   2073     {
   2074       .url_prefix = "/orders/",
   2075       .have_id_segment = true,
   2076       .allow_deleted_instance = true,
   2077       .url_suffix = "refund",
   2078       .method = MHD_HTTP_METHOD_POST,
   2079       .handler = &TMH_post_orders_ID_refund,
   2080       /* the body should be pretty small, allow 1 MB of upload
   2081          to set a conservative bound for sane wallets */
   2082       .max_upload = 1024 * 1024
   2083     },
   2084     /* GET /orders/$ID: */
   2085     {
   2086       .url_prefix = "/orders/",
   2087       .method = MHD_HTTP_METHOD_GET,
   2088       .allow_deleted_instance = true,
   2089       .have_id_segment = true,
   2090       .handler = &TMH_get_orders_ID
   2091     },
   2092     /* GET /static/ *: */
   2093     {
   2094       .url_prefix = "/static/",
   2095       .method = MHD_HTTP_METHOD_GET,
   2096       .have_id_segment = true,
   2097       .handler = &TMH_return_static
   2098     },
   2099     /* GET /templates/$ID/: */
   2100     {
   2101       .url_prefix = "/templates/",
   2102       .method = MHD_HTTP_METHOD_GET,
   2103       .have_id_segment = true,
   2104       .handler = &TMH_get_templates_ID
   2105     },
   2106     /* GET /products/$HASH/image: */
   2107     {
   2108       .url_prefix = "/products/",
   2109       .method = MHD_HTTP_METHOD_GET,
   2110       .have_id_segment = true,
   2111       .allow_deleted_instance = true,
   2112       .url_suffix = "image",
   2113       .handler = &TMH_get_products_image
   2114     },
   2115     /* POST /templates/$ID: */
   2116     {
   2117       .url_prefix = "/templates/",
   2118       .method = MHD_HTTP_METHOD_POST,
   2119       .have_id_segment = true,
   2120       .handler = &TMH_post_using_templates_ID,
   2121       .max_upload = 1024 * 1024
   2122     },
   2123     /* POST /challenge/$ID: */
   2124     {
   2125       .url_prefix = "/challenge/",
   2126       .method = MHD_HTTP_METHOD_POST,
   2127       .have_id_segment = true,
   2128       .handler = &TMH_post_challenge_ID,
   2129       .max_upload = 1024
   2130     },
   2131     /* POST /challenge/$ID/confirm: */
   2132     {
   2133       .url_prefix = "/challenge/",
   2134       .method = MHD_HTTP_METHOD_POST,
   2135       .have_id_segment = true,
   2136       .url_suffix = "confirm",
   2137       .handler = &TMH_post_challenge_ID_confirm,
   2138       .max_upload = 1024
   2139     },
   2140     /* POST /instances */
   2141     {
   2142       .url_prefix = "/instances",
   2143       .method = MHD_HTTP_METHOD_POST,
   2144       .skip_instance = true,
   2145       .default_only = true,
   2146       .handler = &TMH_public_post_instances,
   2147       /* allow instance data of up to 8 MB, that should be plenty;
   2148          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   2149          would require further changes to the allocation logic
   2150          in the code... */
   2151       .max_upload = 1024 * 1024 * 8
   2152     },
   2153     /* POST /forgot-password: */
   2154     {
   2155       .url_prefix = "/forgot-password",
   2156       .method = MHD_HTTP_METHOD_POST,
   2157       .handler = &TMH_public_post_instances_ID_auth,
   2158       /* Body should be pretty small. */
   2159       .max_upload = 1024 * 1024
   2160     },
   2161     {
   2162       .url_prefix = "*",
   2163       .method = MHD_HTTP_METHOD_OPTIONS,
   2164       .handler = &handle_server_options
   2165     },
   2166     {
   2167       .url_prefix = NULL
   2168     }
   2169   };
   2170   struct TMH_HandlerContext *hc = *con_cls;
   2171   struct TMH_RequestHandler *handlers;
   2172   bool use_default = false;
   2173 
   2174   (void) cls;
   2175   (void) version;
   2176   if (NULL != hc->url)
   2177   {
   2178     /* MHD calls us again for a request, for first call
   2179        see 'else' case below */
   2180     GNUNET_assert (NULL != hc->rh);
   2181     GNUNET_SCHEDULER_begin_async_scope (&hc->async_scope_id);
   2182     if ( (hc->has_body) &&
   2183          (NULL == hc->request_body) )
   2184     {
   2185       size_t mul = hc->rh->max_upload;
   2186       enum GNUNET_GenericReturnValue res;
   2187 
   2188       if (0 == mul)
   2189         mul = DEFAULT_MAX_UPLOAD_SIZE;
   2190       if ( (hc->total_upload + *upload_data_size < hc->total_upload) ||
   2191            (hc->total_upload + *upload_data_size > mul) )
   2192       {
   2193         /* Client exceeds upload limit. Should _usually_ be checked earlier
   2194            when we look at the MHD_HTTP_HEADER_CONTENT_LENGTH, alas with
   2195            chunked encoding an uploader MAY have omitted this, and thus
   2196            not permitted us to check on time. In this case, we just close
   2197            the connection once it exceeds our limit (instead of waiting
   2198            for the upload to complete and then fail). This could theoretically
   2199            cause some clients to retry, alas broken or malicious clients
   2200            are likely to retry anyway, so little we can do about it, and
   2201            failing earlier seems the best option here.  */
   2202         GNUNET_break_op (0);
   2203         return MHD_NO;
   2204       }
   2205       hc->total_upload += *upload_data_size;
   2206       res = TALER_MHD_parse_post_json (connection,
   2207                                        &hc->json_parse_context,
   2208                                        upload_data,
   2209                                        upload_data_size,
   2210                                        &hc->request_body);
   2211       if (GNUNET_SYSERR == res)
   2212         return MHD_NO;
   2213       /* A error response was already generated */
   2214       if ( (GNUNET_NO == res) ||
   2215            /* or, need more data to accomplish parsing */
   2216            (NULL == hc->request_body) )
   2217         return MHD_YES; /* let MHD call us *again* */
   2218     }
   2219     /* Upload complete (if any), call handler to generate reply */
   2220     return hc->rh->handler (hc->rh,
   2221                             connection,
   2222                             hc);
   2223   }
   2224   hc->url = url;
   2225   {
   2226     const char *correlation_id;
   2227 
   2228     correlation_id = MHD_lookup_connection_value (connection,
   2229                                                   MHD_HEADER_KIND,
   2230                                                   "Taler-Correlation-Id");
   2231     if ( (NULL != correlation_id) &&
   2232          (GNUNET_YES !=
   2233           GNUNET_CURL_is_valid_scope_id (correlation_id)) )
   2234     {
   2235       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2236                   "Illegal incoming correlation ID\n");
   2237       correlation_id = NULL;
   2238     }
   2239     if (NULL != correlation_id)
   2240       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2241                   "Handling request for (%s) URL '%s', correlation_id=%s\n",
   2242                   method,
   2243                   url,
   2244                   correlation_id);
   2245     else
   2246       GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2247                   "Handling request (%s) for URL '%s'\n",
   2248                   method,
   2249                   url);
   2250   }
   2251 
   2252   if (0 == strcasecmp (method,
   2253                        MHD_HTTP_METHOD_HEAD))
   2254     method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
   2255 
   2256 
   2257   /* Find out the merchant backend instance for the request.
   2258    * If there is an instance, remove the instance specification
   2259    * from the beginning of the request URL. */
   2260   {
   2261     const char *instance_prefix = "/instances/";
   2262 
   2263     if (0 == strncmp (url,
   2264                       instance_prefix,
   2265                       strlen (instance_prefix)))
   2266     {
   2267       /* url starts with "/instances/" */
   2268       const char *istart = url + strlen (instance_prefix);
   2269       const char *slash = strchr (istart, '/');
   2270       char *instance_id;
   2271 
   2272       if (NULL == slash)
   2273         instance_id = GNUNET_strdup (istart);
   2274       else
   2275         instance_id = GNUNET_strndup (istart,
   2276                                       slash - istart);
   2277       if (0 == strcmp (instance_id,
   2278                        "admin"))
   2279       {
   2280         MHD_RESULT ret;
   2281         struct MHD_Response *response;
   2282         const char *rstart = hc->full_url + strlen (instance_prefix);
   2283         const char *rslash = strchr (rstart, '/');
   2284 
   2285         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2286                     "Client used deprecated '/instances/default/' path. Redirecting to modern path\n");
   2287 
   2288         response
   2289           = MHD_create_response_from_buffer (0,
   2290                                              NULL,
   2291                                              MHD_RESPMEM_PERSISTENT);
   2292         TALER_MHD_add_global_headers (response,
   2293                                       true);
   2294         if (MHD_NO ==
   2295             MHD_add_response_header (response,
   2296                                      MHD_HTTP_HEADER_LOCATION,
   2297                                      NULL == rslash
   2298                                      ? "/"
   2299                                      : rslash))
   2300         {
   2301           GNUNET_break (0);
   2302           MHD_destroy_response (response);
   2303           GNUNET_free (instance_id);
   2304           return MHD_NO;
   2305         }
   2306         ret = MHD_queue_response (connection,
   2307                                   MHD_HTTP_PERMANENT_REDIRECT,
   2308                                   response);
   2309         MHD_destroy_response (response);
   2310         GNUNET_free (instance_id);
   2311         return ret;
   2312       }
   2313       hc->instance = TMH_lookup_instance (instance_id);
   2314       if ( (NULL == hc->instance) &&
   2315            (0 == strcmp ("admin",
   2316                          instance_id)) )
   2317         hc->instance = TMH_lookup_instance (NULL);
   2318       GNUNET_free (instance_id);
   2319       if (NULL == slash)
   2320         url = "";
   2321       else
   2322         url = slash;
   2323     }
   2324     else
   2325     {
   2326       /* use 'default' */
   2327       use_default = true;
   2328       hc->instance = TMH_lookup_instance (NULL);
   2329     }
   2330     if (NULL != hc->instance)
   2331     {
   2332       GNUNET_assert (hc->instance->rc < UINT_MAX);
   2333       hc->instance->rc++;
   2334     }
   2335   }
   2336 
   2337   {
   2338     const char *management_prefix = "/management/";
   2339     const char *private_prefix = "/private/";
   2340 
   2341     if ( (0 == strncmp (url,
   2342                         management_prefix,
   2343                         strlen (management_prefix))) )
   2344     {
   2345       handlers = management_handlers;
   2346       url += strlen (management_prefix) - 1;
   2347     }
   2348     else if ( (0 == strncmp (url,
   2349                              private_prefix,
   2350                              strlen (private_prefix))) ||
   2351               (0 == strcmp (url,
   2352                             "/private")) )
   2353     {
   2354       handlers = private_handlers;
   2355       if (0 == strcmp (url,
   2356                        "/private"))
   2357         url = "/";
   2358       else
   2359         url += strlen (private_prefix) - 1;
   2360     }
   2361     else
   2362     {
   2363       handlers = public_handlers;
   2364     }
   2365   }
   2366 
   2367   if (0 == strcmp (url,
   2368                    ""))
   2369     url = "/"; /* code below does not like empty string */
   2370 
   2371   {
   2372     /* Matching URL found, but maybe method doesn't match */
   2373     size_t prefix_strlen; /* i.e. 8 for "/orders/", or 7 for "/config" */
   2374     const char *infix_url = NULL; /* i.e. "$ORDER_ID", no '/'-es */
   2375     size_t infix_strlen = 0; /* number of characters in infix_url */
   2376     const char *suffix_url = NULL; /* i.e. "refund", excludes '/' at the beginning */
   2377     size_t suffix_strlen = 0; /* number of characters in suffix_url */
   2378 
   2379     /* parse the URL into the three different components */
   2380     {
   2381       const char *slash;
   2382 
   2383       slash = strchr (&url[1], '/');
   2384       if (NULL == slash)
   2385       {
   2386         /* the prefix was everything */
   2387         prefix_strlen = strlen (url);
   2388       }
   2389       else
   2390       {
   2391         prefix_strlen = slash - url + 1; /* includes both '/'-es if present! */
   2392         infix_url = slash + 1;
   2393         slash = strchr (infix_url, '/');
   2394         if (NULL == slash)
   2395         {
   2396           /* the infix was the rest */
   2397           infix_strlen = strlen (infix_url);
   2398         }
   2399         else
   2400         {
   2401           infix_strlen = slash - infix_url; /* excludes both '/'-es */
   2402           suffix_url = slash + 1; /* skip the '/' */
   2403           suffix_strlen = strlen (suffix_url);
   2404         }
   2405         hc->infix = GNUNET_strndup (infix_url,
   2406                                     infix_strlen);
   2407       }
   2408     }
   2409 
   2410     /* find matching handler */
   2411     {
   2412       bool url_found = false;
   2413 
   2414       for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
   2415       {
   2416         struct TMH_RequestHandler *rh = &handlers[i];
   2417 
   2418         if (rh->default_only && (! use_default))
   2419           continue;
   2420         if (! prefix_match (rh,
   2421                             url,
   2422                             prefix_strlen,
   2423                             infix_url,
   2424                             infix_strlen,
   2425                             suffix_url,
   2426                             suffix_strlen))
   2427           continue;
   2428         url_found = true;
   2429         if (0 == strcasecmp (method,
   2430                              MHD_HTTP_METHOD_OPTIONS))
   2431         {
   2432           return TALER_MHD_reply_cors_preflight (connection);
   2433         }
   2434         if ( (rh->method != NULL) &&
   2435              (0 != strcasecmp (method,
   2436                                rh->method)) )
   2437           continue;
   2438         hc->rh = rh;
   2439         break;
   2440       }
   2441       /* Handle HTTP 405: METHOD NOT ALLOWED case */
   2442       if ( (NULL == hc->rh) &&
   2443            (url_found) )
   2444       {
   2445         struct MHD_Response *reply;
   2446         MHD_RESULT ret;
   2447         char *allowed = NULL;
   2448 
   2449         GNUNET_break_op (0);
   2450         /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */
   2451         for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
   2452         {
   2453           struct TMH_RequestHandler *rh = &handlers[i];
   2454 
   2455           if (rh->default_only && (! use_default))
   2456             continue;
   2457           if (! prefix_match (rh,
   2458                               url,
   2459                               prefix_strlen,
   2460                               infix_url,
   2461                               infix_strlen,
   2462                               suffix_url,
   2463                               suffix_strlen))
   2464             continue;
   2465           if (NULL == allowed)
   2466           {
   2467             allowed = GNUNET_strdup (rh->method);
   2468           }
   2469           else
   2470           {
   2471             char *tmp;
   2472 
   2473             GNUNET_asprintf (&tmp,
   2474                              "%s, %s",
   2475                              allowed,
   2476                              rh->method);
   2477             GNUNET_free (allowed);
   2478             allowed = tmp;
   2479           }
   2480           if (0 == strcasecmp (rh->method,
   2481                                MHD_HTTP_METHOD_GET))
   2482           {
   2483             char *tmp;
   2484 
   2485             GNUNET_asprintf (&tmp,
   2486                              "%s, %s",
   2487                              allowed,
   2488                              MHD_HTTP_METHOD_HEAD);
   2489             GNUNET_free (allowed);
   2490             allowed = tmp;
   2491           }
   2492         }
   2493         reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
   2494                                       method);
   2495         GNUNET_break (MHD_YES ==
   2496                       MHD_add_response_header (reply,
   2497                                                MHD_HTTP_HEADER_ALLOW,
   2498                                                allowed));
   2499         GNUNET_free (allowed);
   2500         ret = MHD_queue_response (connection,
   2501                                   MHD_HTTP_METHOD_NOT_ALLOWED,
   2502                                   reply);
   2503         MHD_destroy_response (reply);
   2504         return ret;
   2505       }
   2506       if (NULL == hc->rh)
   2507       {
   2508         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2509                     "Endpoint `%s' not known\n",
   2510                     hc->url);
   2511         return TALER_MHD_reply_with_error (connection,
   2512                                            MHD_HTTP_NOT_FOUND,
   2513                                            TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
   2514                                            hc->url);
   2515       }
   2516     }
   2517   }
   2518   /* At this point, we must have found a handler */
   2519   GNUNET_assert (NULL != hc->rh);
   2520 
   2521   /* If an instance should be there, check one exists */
   2522   if ( (NULL == hc->instance) &&
   2523        (! hc->rh->skip_instance) )
   2524   {
   2525     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2526                 "Instance for `%s' not known\n",
   2527                 hc->url);
   2528     return TALER_MHD_reply_with_error (connection,
   2529                                        MHD_HTTP_NOT_FOUND,
   2530                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
   2531                                        hc->infix);
   2532   }
   2533 
   2534   /* Access control for non-public handlers */
   2535   if (public_handlers != handlers)
   2536   {
   2537     const char *auth;
   2538     bool is_basic_auth = false;
   2539     bool auth_malformed = false;
   2540 
   2541     auth = MHD_lookup_connection_value (connection,
   2542                                         MHD_HEADER_KIND,
   2543                                         MHD_HTTP_HEADER_AUTHORIZATION);
   2544 
   2545     if (NULL != auth)
   2546     {
   2547       extract_auth (&auth,
   2548                     &is_basic_auth);
   2549       if (NULL == auth)
   2550         auth_malformed = true;
   2551       hc->auth_token = auth;
   2552     }
   2553 
   2554     /* If we have zero configured instances (not even ones that have been
   2555        purged) or explicitly disabled authentication, THEN we accept anything
   2556        (no access control), as we then also have no data to protect. */
   2557     if ((0 == GNUNET_CONTAINER_multihashmap_size (TMH_by_id_map)) ||
   2558         (GNUNET_YES == TMH_auth_disabled))
   2559     {
   2560       hc->auth_scope = TMH_AS_ALL;
   2561     }
   2562     else if (is_basic_auth)
   2563     {
   2564       process_basic_auth (hc, auth);
   2565     }
   2566     else /* Check bearer token */
   2567     {
   2568       enum TALER_ErrorCode ec;
   2569 
   2570       ec = process_bearer_auth (hc,
   2571                                 auth);
   2572       if (TALER_EC_NONE != ec)
   2573       {
   2574         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2575                     "Bearer authentication failed: %d\n",
   2576                     (int) ec);
   2577         return TALER_MHD_reply_with_ec (connection,
   2578                                         ec,
   2579                                         NULL);
   2580       }
   2581     }
   2582     /* We grant access if:
   2583        - Endpoint does not require permissions
   2584        - Authorization scope of bearer token contains permissions
   2585          required by endpoint.
   2586      */
   2587     if ( (NULL != hc->rh->permission) &&
   2588          (! permission_in_scope (hc->rh->permission,
   2589                                  hc->auth_scope)))
   2590     {
   2591       if (auth_malformed &&
   2592           (TMH_AS_NONE == hc->auth_scope) )
   2593       {
   2594         GNUNET_break_op (0);
   2595         return TALER_MHD_reply_with_error (
   2596           connection,
   2597           MHD_HTTP_UNAUTHORIZED,
   2598           TALER_EC_GENERIC_PARAMETER_MALFORMED,
   2599           "'" RFC_8959_PREFIX
   2600           "' prefix or 'Bearer' missing in 'Authorization' header");
   2601       }
   2602       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2603                   "Credentials provided are %d which are insufficient for access to `%s'\n",
   2604                   (int) hc->auth_scope,
   2605                   hc->rh->permission);
   2606       return TALER_MHD_reply_with_error (connection,
   2607                                          MHD_HTTP_UNAUTHORIZED,
   2608                                          TALER_EC_MERCHANT_GENERIC_UNAUTHORIZED,
   2609                                          "Check credentials in 'Authorization' header");
   2610     }
   2611   } /* if (use_private) */
   2612 
   2613 
   2614   if ( (NULL == hc->instance) &&
   2615        (! hc->rh->skip_instance) )
   2616   {
   2617     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2618                 "Instance for URL `%s' not known\n",
   2619                 url);
   2620     return TALER_MHD_reply_with_error (connection,
   2621                                        MHD_HTTP_NOT_FOUND,
   2622                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_UNKNOWN,
   2623                                        url);
   2624   }
   2625   if ( (NULL != hc->instance) && /* make static analysis happy */
   2626        (! hc->rh->skip_instance) &&
   2627        (hc->instance->deleted) &&
   2628        (! hc->rh->allow_deleted_instance) )
   2629   {
   2630     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2631                 "Instance `%s' was deleted\n",
   2632                 hc->instance->settings.id);
   2633     return TALER_MHD_reply_with_error (connection,
   2634                                        MHD_HTTP_NOT_FOUND,
   2635                                        TALER_EC_MERCHANT_GENERIC_INSTANCE_DELETED,
   2636                                        hc->instance->settings.id);
   2637   }
   2638   /* parse request body */
   2639   hc->has_body = ( (0 == strcasecmp (method,
   2640                                      MHD_HTTP_METHOD_POST)) ||
   2641                    /* PUT is not yet used */
   2642                    (0 == strcasecmp (method,
   2643                                      MHD_HTTP_METHOD_PATCH)) );
   2644   if (hc->has_body)
   2645   {
   2646     TALER_MHD_check_content_length (connection,
   2647                                     0 == hc->rh->max_upload
   2648                                     ? DEFAULT_MAX_UPLOAD_SIZE
   2649                                     : hc->rh->max_upload);
   2650     GNUNET_break (NULL == hc->request_body); /* can't have it already */
   2651   }
   2652   return MHD_YES; /* wait for MHD to call us again */
   2653 }
   2654 
   2655 
   2656 /**
   2657  * Callback invoked with information about a bank account.
   2658  *
   2659  * @param cls closure with a `struct TMH_MerchantInstance *`
   2660  * @param merchant_priv private key of the merchant instance
   2661  * @param acc details about the account
   2662  */
   2663 static void
   2664 add_account_cb (void *cls,
   2665                 const struct TALER_MerchantPrivateKeyP *merchant_priv,
   2666                 const struct TALER_MERCHANTDB_AccountDetails *acc)
   2667 {
   2668   struct TMH_MerchantInstance *mi = cls;
   2669   struct TMH_WireMethod *wm;
   2670 
   2671   (void) merchant_priv;
   2672   wm = GNUNET_new (struct TMH_WireMethod);
   2673   wm->h_wire = acc->h_wire;
   2674   wm->payto_uri.full_payto
   2675     = GNUNET_strdup (acc->payto_uri.full_payto);
   2676   wm->wire_salt = acc->salt;
   2677   wm->wire_method
   2678     = TALER_payto_get_method (acc->payto_uri.full_payto);
   2679   wm->active = acc->active;
   2680   GNUNET_CONTAINER_DLL_insert (mi->wm_head,
   2681                                mi->wm_tail,
   2682                                wm);
   2683 }
   2684 
   2685 
   2686 /**
   2687  * Function called during startup to add all known instances to our
   2688  * hash map in memory for faster lookups when we receive requests.
   2689  *
   2690  * @param cls closure, NULL, unused
   2691  * @param merchant_pub public key of the instance
   2692  * @param merchant_priv private key of the instance, NULL if not available
   2693  * @param is detailed configuration settings for the instance
   2694  * @param ias authentication settings for the instance
   2695  */
   2696 static void
   2697 add_instance_cb (void *cls,
   2698                  const struct TALER_MerchantPublicKeyP *merchant_pub,
   2699                  const struct TALER_MerchantPrivateKeyP *merchant_priv,
   2700                  const struct TALER_MERCHANTDB_InstanceSettings *is,
   2701                  const struct TALER_MERCHANTDB_InstanceAuthSettings *ias)
   2702 {
   2703   struct TMH_MerchantInstance *mi;
   2704   enum GNUNET_DB_QueryStatus qs;
   2705 
   2706   (void) cls;
   2707   mi = TMH_lookup_instance (is->id);
   2708   if (NULL != mi)
   2709   {
   2710     /* (outdated) entry exists, remove old entry */
   2711     (void) TMH_instance_free_cb (NULL,
   2712                                  &mi->h_instance,
   2713                                  mi);
   2714   }
   2715   mi = GNUNET_new (struct TMH_MerchantInstance);
   2716   mi->settings = *is;
   2717   mi->auth = *ias;
   2718   mi->settings.id = GNUNET_strdup (mi->settings.id);
   2719   mi->settings.name = GNUNET_strdup (mi->settings.name);
   2720   if (NULL != mi->settings.email)
   2721     mi->settings.email = GNUNET_strdup (mi->settings.email);
   2722   if (NULL != mi->settings.phone)
   2723     mi->settings.phone = GNUNET_strdup (mi->settings.phone);
   2724   if (NULL != mi->settings.website)
   2725     mi->settings.website = GNUNET_strdup (mi->settings.website);
   2726   if (NULL != mi->settings.logo)
   2727     mi->settings.logo = GNUNET_strdup (mi->settings.logo);
   2728   mi->settings.address = json_incref (mi->settings.address);
   2729   mi->settings.jurisdiction = json_incref (mi->settings.jurisdiction);
   2730   if (NULL != merchant_priv)
   2731     mi->merchant_priv = *merchant_priv;
   2732   else
   2733     mi->deleted = true;
   2734   mi->merchant_pub = *merchant_pub;
   2735   qs = TMH_db->select_accounts (TMH_db->cls,
   2736                                 mi->settings.id,
   2737                                 &add_account_cb,
   2738                                 mi);
   2739   if (0 > qs)
   2740   {
   2741     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2742                 "Error loading accounts of `%s' from database\n",
   2743                 mi->settings.id);
   2744   }
   2745   GNUNET_assert (GNUNET_OK ==
   2746                  TMH_add_instance (mi));
   2747 }
   2748 
   2749 
   2750 /**
   2751  * Trigger (re)loading of instance settings from DB.
   2752  *
   2753  * @param cls NULL
   2754  * @param extra ID of the instance that changed, NULL
   2755  *              to load all instances (will not handle purges!)
   2756  * @param extra_len number of bytes in @a extra
   2757  */
   2758 static void
   2759 load_instances (void *cls,
   2760                 const void *extra,
   2761                 size_t extra_len)
   2762 {
   2763   enum GNUNET_DB_QueryStatus qs;
   2764   const char *id = extra;
   2765 
   2766   (void) cls;
   2767   if ( (NULL != extra) &&
   2768        ( (0 == extra_len) ||
   2769          ('\0' != id[extra_len - 1]) ) )
   2770   {
   2771     GNUNET_break (0 == extra_len);
   2772     extra = NULL;
   2773   }
   2774   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2775               "Received instance settings notification: reload `%s'\n",
   2776               id);
   2777   if (NULL == extra)
   2778   {
   2779     qs = TMH_db->lookup_instances (TMH_db->cls,
   2780                                    false,
   2781                                    &add_instance_cb,
   2782                                    NULL);
   2783   }
   2784   else
   2785   {
   2786     struct TMH_MerchantInstance *mi;
   2787 
   2788     /* This must be done here to handle instance
   2789        purging, as for purged instances, the DB
   2790        lookup below will otherwise do nothing */
   2791     mi = TMH_lookup_instance (id);
   2792     if (NULL != mi)
   2793     {
   2794       (void) TMH_instance_free_cb (NULL,
   2795                                    &mi->h_instance,
   2796                                    mi);
   2797     }
   2798     qs = TMH_db->lookup_instance (TMH_db->cls,
   2799                                   id,
   2800                                   false,
   2801                                   &add_instance_cb,
   2802                                   NULL);
   2803   }
   2804   if (0 > qs)
   2805   {
   2806     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2807                 "Failed initialization. Check database setup.\n");
   2808     global_ret = EXIT_NOPERMISSION;
   2809     GNUNET_SCHEDULER_shutdown ();
   2810     return;
   2811   }
   2812 }
   2813 
   2814 
   2815 /**
   2816  * A transaction modified an instance setting (or created/deleted/purged
   2817  * one). Notify all backends about the change.
   2818  *
   2819  * @param id ID of the instance that changed
   2820  */
   2821 void
   2822 TMH_reload_instances (const char *id)
   2823 {
   2824   struct GNUNET_DB_EventHeaderP es = {
   2825     .size = ntohs (sizeof (es)),
   2826     .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
   2827   };
   2828 
   2829   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2830               "Generating instance settings notification: reload `%s'\n",
   2831               id);
   2832   TMH_db->event_notify (TMH_db->cls,
   2833                         &es,
   2834                         id,
   2835                         (NULL == id)
   2836                         ? 0
   2837                         : strlen (id) + 1);
   2838 }
   2839 
   2840 
   2841 /**
   2842  * Callback invoked on every listen socket to start the
   2843  * respective MHD HTTP daemon.
   2844  *
   2845  * @param cls unused
   2846  * @param lsock the listen socket
   2847  */
   2848 static void
   2849 start_daemon (void *cls,
   2850               int lsock)
   2851 {
   2852   struct MHD_Daemon *mhd;
   2853 
   2854   (void) cls;
   2855   GNUNET_assert (-1 != lsock);
   2856   mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME | MHD_USE_DUAL_STACK
   2857                           | MHD_USE_AUTO,
   2858                           0 /* port */,
   2859                           NULL, NULL,
   2860                           &url_handler, NULL,
   2861                           MHD_OPTION_LISTEN_SOCKET, lsock,
   2862                           MHD_OPTION_URI_LOG_CALLBACK,
   2863                           &full_url_track_callback, NULL,
   2864                           MHD_OPTION_NOTIFY_COMPLETED,
   2865                           &handle_mhd_completion_callback, NULL,
   2866                           MHD_OPTION_CONNECTION_TIMEOUT,
   2867                           (unsigned int) 10 /* 10s */,
   2868                           MHD_OPTION_END);
   2869   if (NULL == mhd)
   2870   {
   2871     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   2872                 "Failed to launch HTTP service.\n");
   2873     GNUNET_SCHEDULER_shutdown ();
   2874     return;
   2875   }
   2876   have_daemons = true;
   2877   TALER_MHD_daemon_start (mhd);
   2878 }
   2879 
   2880 
   2881 /**
   2882  * Main function that will be run by the scheduler.
   2883  *
   2884  * @param cls closure
   2885  * @param args remaining command-line arguments
   2886  * @param cfgfile name of the configuration file used (for saving, can be
   2887  *        NULL!)
   2888  * @param config configuration
   2889  */
   2890 static void
   2891 run (void *cls,
   2892      char *const *args,
   2893      const char *cfgfile,
   2894      const struct GNUNET_CONFIGURATION_Handle *config)
   2895 {
   2896   enum TALER_MHD_GlobalOptions go;
   2897   int elen;
   2898 
   2899   (void) cls;
   2900   (void) args;
   2901   (void) cfgfile;
   2902   cfg = config;
   2903   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   2904               "Starting taler-merchant-httpd\n");
   2905   go = TALER_MHD_GO_NONE;
   2906   if (merchant_connection_close)
   2907     go |= TALER_MHD_GO_FORCE_CONNECTION_CLOSE;
   2908   TALER_MHD_setup (go);
   2909 
   2910   global_ret = EXIT_SUCCESS;
   2911   GNUNET_SCHEDULER_add_shutdown (&do_shutdown,
   2912                                  NULL);
   2913 
   2914   TMH_curl_ctx
   2915     = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
   2916                         &merchant_curl_rc);
   2917   if (NULL == TMH_curl_ctx)
   2918   {
   2919     GNUNET_break (0);
   2920     global_ret = EXIT_NO_RESTART;
   2921     GNUNET_SCHEDULER_shutdown ();
   2922     return;
   2923   }
   2924   merchant_curl_rc = GNUNET_CURL_gnunet_rc_create (TMH_curl_ctx);
   2925   /* Disable 100 continue processing */
   2926   GNUNET_break (GNUNET_OK ==
   2927                 GNUNET_CURL_append_header (TMH_curl_ctx,
   2928                                            MHD_HTTP_HEADER_EXPECT ":"));
   2929   GNUNET_CURL_enable_async_scope_header (TMH_curl_ctx,
   2930                                          "Taler-Correlation-Id");
   2931 
   2932   if (GNUNET_SYSERR ==
   2933       TALER_config_get_currency (cfg,
   2934                                  "merchant",
   2935                                  &TMH_currency))
   2936   {
   2937     global_ret = EXIT_NOTCONFIGURED;
   2938     GNUNET_SCHEDULER_shutdown ();
   2939     return;
   2940   }
   2941   if (GNUNET_OK !=
   2942       TALER_CONFIG_parse_currencies (cfg,
   2943                                      TMH_currency,
   2944                                      &TMH_num_cspecs,
   2945                                      &TMH_cspecs))
   2946   {
   2947     global_ret = EXIT_NOTCONFIGURED;
   2948     GNUNET_SCHEDULER_shutdown ();
   2949     return;
   2950   }
   2951 
   2952   if (GNUNET_SYSERR ==
   2953       (TMH_strict_v19
   2954          = GNUNET_CONFIGURATION_get_value_yesno (cfg,
   2955                                                  "merchant",
   2956                                                  "STRICT_PROTOCOL_V19")))
   2957   {
   2958     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   2959                                "merchant",
   2960                                "STRICT_PROTOCOL_V19");
   2961     TMH_strict_v19 = GNUNET_NO;
   2962   }
   2963   if (GNUNET_SYSERR ==
   2964       (TMH_auth_disabled = GNUNET_CONFIGURATION_get_value_yesno (cfg,
   2965                                                                  "merchant",
   2966                                                                  "DISABLE_AUTHENTICATION")))
   2967   {
   2968     TMH_auth_disabled = GNUNET_NO;
   2969   }
   2970   if (GNUNET_YES == TMH_auth_disabled)
   2971   {
   2972     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   2973                 "DANGEROUS: Endpoint Authentication disabled!");
   2974   }
   2975 
   2976   if (GNUNET_SYSERR ==
   2977       (TMH_have_self_provisioning
   2978          = GNUNET_CONFIGURATION_get_value_yesno (cfg,
   2979                                                  "merchant",
   2980                                                  "ENABLE_SELF_PROVISIONING")))
   2981   {
   2982     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   2983                                "merchant",
   2984                                "ENABLE_SELF_PROVISIONING");
   2985     TMH_have_self_provisioning = GNUNET_NO;
   2986   }
   2987 
   2988   if (GNUNET_OK !=
   2989       GNUNET_CONFIGURATION_get_value_time (cfg,
   2990                                            "merchant",
   2991                                            "LEGAL_PRESERVATION",
   2992                                            &TMH_legal_expiration))
   2993   {
   2994     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
   2995                                "merchant",
   2996                                "LEGAL_PRESERVATION");
   2997     global_ret = EXIT_NOTCONFIGURED;
   2998     GNUNET_SCHEDULER_shutdown ();
   2999     return;
   3000   }
   3001 
   3002   if (GNUNET_OK !=
   3003       GNUNET_CONFIGURATION_get_value_time (cfg,
   3004                                            "merchant",
   3005                                            "DEFAULT_PAY_DELAY",
   3006                                            &TMH_default_pay_delay))
   3007   {
   3008     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   3009                                "merchant",
   3010                                "DEFAULT_PAY_DELAY");
   3011     TMH_default_pay_delay = GNUNET_TIME_UNIT_DAYS;
   3012   }
   3013   if (GNUNET_TIME_relative_is_forever (TMH_default_pay_delay))
   3014   {
   3015     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   3016                                "merchant",
   3017                                "DEFAULT_PAY_DELAY",
   3018                                "forever is not allowed");
   3019     global_ret = EXIT_NOTCONFIGURED;
   3020     GNUNET_SCHEDULER_shutdown ();
   3021     return;
   3022   }
   3023   if (GNUNET_OK !=
   3024       GNUNET_CONFIGURATION_get_value_time (cfg,
   3025                                            "merchant",
   3026                                            "DEFAULT_REFUND_DELAY",
   3027                                            &TMH_default_refund_delay))
   3028   {
   3029     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   3030                                "merchant",
   3031                                "DEFAULT_REFUND_DELAY");
   3032     TMH_default_refund_delay = GNUNET_TIME_relative_multiply (
   3033       GNUNET_TIME_UNIT_DAYS,
   3034       15);
   3035   }
   3036   if (GNUNET_TIME_relative_is_forever (TMH_default_refund_delay))
   3037   {
   3038     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   3039                                "merchant",
   3040                                "DEFAULT_REFUND_DELAY",
   3041                                "forever is not allowed");
   3042     global_ret = EXIT_NOTCONFIGURED;
   3043     GNUNET_SCHEDULER_shutdown ();
   3044     return;
   3045   }
   3046 
   3047   if (GNUNET_OK !=
   3048       GNUNET_CONFIGURATION_get_value_time (cfg,
   3049                                            "merchant",
   3050                                            "DEFAULT_WIRE_TRANSFER_DELAY",
   3051                                            &TMH_default_wire_transfer_delay))
   3052   {
   3053     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   3054                                "merchant",
   3055                                "DEFAULT_WIRE_TRANSFER_DELAY");
   3056     TMH_default_wire_transfer_delay = GNUNET_TIME_UNIT_MONTHS;
   3057   }
   3058   if (GNUNET_TIME_relative_is_forever (TMH_default_wire_transfer_delay))
   3059   {
   3060     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_INFO,
   3061                                "merchant",
   3062                                "DEFAULT_WIRE_TRANSFER_DELAY",
   3063                                "forever is not allowed");
   3064     global_ret = EXIT_NOTCONFIGURED;
   3065     GNUNET_SCHEDULER_shutdown ();
   3066     return;
   3067   }
   3068 
   3069   {
   3070     char *dwtri;
   3071 
   3072     if (GNUNET_OK !=
   3073         GNUNET_CONFIGURATION_get_value_string (
   3074           cfg,
   3075           "merchant",
   3076           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
   3077           &dwtri))
   3078     {
   3079       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_INFO,
   3080                                  "merchant",
   3081                                  "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL");
   3082       TMH_default_wire_transfer_rounding_interval = GNUNET_TIME_RI_NONE;
   3083     }
   3084     else
   3085     {
   3086       if (GNUNET_OK !=
   3087           GNUNET_TIME_string_to_round_interval (
   3088             dwtri,
   3089             &TMH_default_wire_transfer_rounding_interval))
   3090       {
   3091         GNUNET_log_config_invalid (
   3092           GNUNET_ERROR_TYPE_ERROR,
   3093           "merchant",
   3094           "DEFAULT_WIRE_TRANSFER_ROUNDING_INTERVAL",
   3095           "invalid time rounding interval");
   3096         global_ret = EXIT_NOTCONFIGURED;
   3097         GNUNET_free (dwtri);
   3098         GNUNET_SCHEDULER_shutdown ();
   3099         return;
   3100       }
   3101       GNUNET_free (dwtri);
   3102     }
   3103   }
   3104 
   3105   TMH_load_terms (cfg);
   3106 
   3107   if (GNUNET_OK !=
   3108       GNUNET_CONFIGURATION_get_value_string (cfg,
   3109                                              "merchant",
   3110                                              "PAYMENT_TARGET_TYPES",
   3111                                              &TMH_allowed_payment_targets))
   3112   {
   3113     TMH_allowed_payment_targets = GNUNET_strdup ("*");
   3114   }
   3115 
   3116   if (GNUNET_OK !=
   3117       GNUNET_CONFIGURATION_get_value_string (cfg,
   3118                                              "merchant",
   3119                                              "DEFAULT_PERSONA",
   3120                                              &TMH_default_persona))
   3121   {
   3122     TMH_default_persona = GNUNET_strdup ("expert");
   3123   }
   3124 
   3125   if (GNUNET_OK !=
   3126       GNUNET_CONFIGURATION_get_value_string (cfg,
   3127                                              "merchant",
   3128                                              "PAYMENT_TARGET_REGEX",
   3129                                              &TMH_payment_target_regex))
   3130   {
   3131     TMH_payment_target_regex = NULL;
   3132   }
   3133   else
   3134   {
   3135     if (0 == strlen (TMH_payment_target_regex))
   3136     {
   3137       GNUNET_free (TMH_payment_target_regex);
   3138     }
   3139     else
   3140     {
   3141       if (0 != regcomp (&TMH_payment_target_re,
   3142                         TMH_payment_target_regex,
   3143                         REG_NOSUB | REG_EXTENDED))
   3144       {
   3145         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   3146                                    "merchant",
   3147                                    "PAYMENT_TARGET_REGEX",
   3148                                    "malformed regular expression");
   3149         global_ret = EXIT_NOTCONFIGURED;
   3150         GNUNET_free (TMH_payment_target_regex);
   3151         GNUNET_SCHEDULER_shutdown ();
   3152         return;
   3153       }
   3154     }
   3155   }
   3156 
   3157   if (GNUNET_OK !=
   3158       GNUNET_CONFIGURATION_get_value_string (cfg,
   3159                                              "merchant",
   3160                                              "HELPER_SMS",
   3161                                              &TMH_helper_sms))
   3162   {
   3163     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   3164                                "merchant",
   3165                                "HELPER_SMS",
   3166                                "no helper specified");
   3167   }
   3168 
   3169   if (GNUNET_OK !=
   3170       GNUNET_CONFIGURATION_get_value_string (cfg,
   3171                                              "merchant",
   3172                                              "HELPER_EMAIL",
   3173                                              &TMH_helper_email))
   3174   {
   3175     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
   3176                                "merchant",
   3177                                "HELPER_EMAIL",
   3178                                "no helper specified");
   3179   }
   3180 
   3181   {
   3182     char *tan_channels;
   3183 
   3184     if (GNUNET_OK ==
   3185         GNUNET_CONFIGURATION_get_value_string (cfg,
   3186                                                "merchant",
   3187                                                "MANDATORY_TAN_CHANNELS",
   3188                                                &tan_channels))
   3189     {
   3190       for (char *tok = strtok (tan_channels,
   3191                                " ");
   3192            NULL != tok;
   3193            tok = strtok (NULL,
   3194                          " "))
   3195       {
   3196         if (0 == strcasecmp (tok,
   3197                              "sms"))
   3198         {
   3199           if (NULL == TMH_helper_sms)
   3200           {
   3201             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   3202                                        "merchant",
   3203                                        "MANDATORY_TAN_CHANNELS",
   3204                                        "SMS mandatory, but no HELPER_SMS configured");
   3205             global_ret = EXIT_NOTCONFIGURED;
   3206             GNUNET_SCHEDULER_shutdown ();
   3207             GNUNET_free (tan_channels);
   3208             return;
   3209           }
   3210           TEH_mandatory_tan_channels |= TEH_TCS_SMS;
   3211         }
   3212         else if (0 == strcasecmp (tok,
   3213                                   "email"))
   3214         {
   3215           if (NULL == TMH_helper_email)
   3216           {
   3217             GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   3218                                        "merchant",
   3219                                        "MANDATORY_TAN_CHANNELS",
   3220                                        "EMAIL mandatory, but no HELPER_EMAIL configured");
   3221             global_ret = EXIT_NOTCONFIGURED;
   3222             GNUNET_SCHEDULER_shutdown ();
   3223             GNUNET_free (tan_channels);
   3224             return;
   3225           }
   3226           TEH_mandatory_tan_channels |= TEH_TCS_EMAIL;
   3227         }
   3228         else
   3229         {
   3230           GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   3231                                      "merchant",
   3232                                      "MANDATORY_TAN_CHANNELS",
   3233                                      tok);
   3234           global_ret = EXIT_NOTCONFIGURED;
   3235           GNUNET_SCHEDULER_shutdown ();
   3236           GNUNET_free (tan_channels);
   3237           return;
   3238         }
   3239       }
   3240       GNUNET_free (tan_channels);
   3241     }
   3242   }
   3243 
   3244   if (GNUNET_OK ==
   3245       GNUNET_CONFIGURATION_get_value_string (cfg,
   3246                                              "merchant",
   3247                                              "BASE_URL",
   3248                                              &TMH_base_url))
   3249   {
   3250     if (! TALER_is_web_url (TMH_base_url))
   3251     {
   3252       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
   3253                                  "merchant",
   3254                                  "BASE_URL",
   3255                                  "Needs to start with 'http://' or 'https://'");
   3256       global_ret = EXIT_NOTCONFIGURED;
   3257       GNUNET_SCHEDULER_shutdown ();
   3258       return;
   3259     }
   3260   }
   3261   if (GNUNET_OK ==
   3262       GNUNET_CONFIGURATION_get_value_string (cfg,
   3263                                              "merchant",
   3264                                              "BACKOFFICE_SPA_DIR",
   3265                                              &TMH_spa_dir))
   3266   {
   3267     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3268                 "Loading merchant SPA from %s\n",
   3269                 TMH_spa_dir);
   3270   }
   3271   else
   3272   {
   3273     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
   3274                 "Loading merchant SPA from default location\n");
   3275   }
   3276 
   3277   if (GNUNET_YES ==
   3278       GNUNET_CONFIGURATION_get_value_yesno (cfg,
   3279                                             "merchant",
   3280                                             "FORCE_AUDIT"))
   3281     TMH_force_audit = GNUNET_YES;
   3282   if (GNUNET_OK !=
   3283       TALER_TEMPLATING_init (TALER_MERCHANT_project_data ()))
   3284   {
   3285     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3286                 "Failed to setup templates\n");
   3287     global_ret = EXIT_NOTINSTALLED;
   3288     GNUNET_SCHEDULER_shutdown ();
   3289     return;
   3290   }
   3291   if (GNUNET_OK !=
   3292       TMH_spa_init (TMH_spa_dir))
   3293   {
   3294     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3295                 "Failed to load single page app\n");
   3296     global_ret = EXIT_NOTINSTALLED;
   3297     GNUNET_SCHEDULER_shutdown ();
   3298     return;
   3299   }
   3300   /* /static/ is currently not used */
   3301   /* (void) TMH_statics_init (); */
   3302   if (NULL ==
   3303       (TMH_by_id_map = GNUNET_CONTAINER_multihashmap_create (4,
   3304                                                              GNUNET_YES)))
   3305   {
   3306     global_ret = EXIT_FAILURE;
   3307     GNUNET_SCHEDULER_shutdown ();
   3308     return;
   3309   }
   3310   if (NULL ==
   3311       (TMH_db = TALER_MERCHANTDB_plugin_load (cfg)))
   3312   {
   3313     global_ret = EXIT_NOTINSTALLED;
   3314     GNUNET_SCHEDULER_shutdown ();
   3315     return;
   3316   }
   3317   if (GNUNET_OK !=
   3318       TMH_db->connect (TMH_db->cls))
   3319   {
   3320     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3321                 "Failed to connect to database. Consider running taler-merchant-dbinit!\n");
   3322     global_ret = EXIT_FAILURE;
   3323     GNUNET_SCHEDULER_shutdown ();
   3324     return;
   3325   }
   3326   elen = TMH_EXCHANGES_init (config);
   3327   if (GNUNET_SYSERR == elen)
   3328   {
   3329     global_ret = EXIT_NOTCONFIGURED;
   3330     GNUNET_SCHEDULER_shutdown ();
   3331     return;
   3332   }
   3333   if (0 == elen)
   3334   {
   3335     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
   3336                 "Fatal: no trusted exchanges configured. Exiting.\n");
   3337     global_ret = EXIT_NOTCONFIGURED;
   3338     GNUNET_SCHEDULER_shutdown ();
   3339     return;
   3340   }
   3341 
   3342   {
   3343     struct GNUNET_DB_EventHeaderP es = {
   3344       .size = ntohs (sizeof (es)),
   3345       .type = ntohs (TALER_DBEVENT_MERCHANT_INSTANCE_SETTINGS)
   3346     };
   3347 
   3348     instance_eh = TMH_db->event_listen (TMH_db->cls,
   3349                                         &es,
   3350                                         GNUNET_TIME_UNIT_FOREVER_REL,
   3351                                         &load_instances,
   3352                                         NULL);
   3353   }
   3354   load_instances (NULL,
   3355                   NULL,
   3356                   0);
   3357   {
   3358     enum GNUNET_GenericReturnValue ret;
   3359 
   3360     ret = TALER_MHD_listen_bind (cfg,
   3361                                  "merchant",
   3362                                  &start_daemon,
   3363                                  NULL);
   3364     switch (ret)
   3365     {
   3366     case GNUNET_SYSERR:
   3367       global_ret = EXIT_NOTCONFIGURED;
   3368       GNUNET_SCHEDULER_shutdown ();
   3369       return;
   3370     case GNUNET_NO:
   3371       if (! have_daemons)
   3372       {
   3373         global_ret = EXIT_NOTCONFIGURED;
   3374         GNUNET_SCHEDULER_shutdown ();
   3375         return;
   3376       }
   3377       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   3378                   "Could not open all configured listen sockets\n");
   3379       break;
   3380     case GNUNET_OK:
   3381       break;
   3382     }
   3383   }
   3384   global_ret = EXIT_SUCCESS;
   3385 }
   3386 
   3387 
   3388 /**
   3389  * The main function of the serve tool
   3390  *
   3391  * @param argc number of arguments from the command line
   3392  * @param argv command line arguments
   3393  * @return 0 ok, non-zero on error
   3394  */
   3395 int
   3396 main (int argc,
   3397       char *const *argv)
   3398 {
   3399   enum GNUNET_GenericReturnValue res;
   3400   struct GNUNET_GETOPT_CommandLineOption options[] = {
   3401     GNUNET_GETOPT_option_flag ('C',
   3402                                "connection-close",
   3403                                "force HTTP connections to be closed after each request",
   3404                                &merchant_connection_close),
   3405     GNUNET_GETOPT_option_timetravel ('T',
   3406                                      "timetravel"),
   3407     GNUNET_GETOPT_option_version (PACKAGE_VERSION "-" VCS_VERSION),
   3408     GNUNET_GETOPT_OPTION_END
   3409   };
   3410 
   3411   res = GNUNET_PROGRAM_run (
   3412     TALER_MERCHANT_project_data (),
   3413     argc, argv,
   3414     "taler-merchant-httpd",
   3415     "Taler merchant's HTTP backend interface",
   3416     options,
   3417     &run, NULL);
   3418   if (GNUNET_SYSERR == res)
   3419     return EXIT_INVALIDARGUMENT;
   3420   if (GNUNET_NO == res)
   3421     return EXIT_SUCCESS;
   3422   return global_ret;
   3423 }