merchant

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

taler-merchant-httpd_dispatcher.c (56247B)


      1 /*
      2   This file is part of TALER
      3   (C) 2014-2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Affero General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file src/backend/taler-merchant-httpd_dispatcher.c
     18  * @brief map requested URL and method to the respective request handler
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include "taler-merchant-httpd_get-config.h"
     23 #include "taler-merchant-httpd_get-exchanges.h"
     24 #include "taler-merchant-httpd_dispatcher.h"
     25 #include "taler-merchant-httpd_get-orders-ORDER_ID.h"
     26 #include "taler-merchant-httpd_get-sessions-SESSION_ID.h"
     27 #include "taler-merchant-httpd_get-products-IMAGE_HASH-image.h"
     28 #include "taler-merchant-httpd_get-templates-TEMPLATE_ID.h"
     29 #include "taler-merchant-httpd_mhd.h"
     30 #include "taler-merchant-httpd_delete-private-accounts-H_WIRE.h"
     31 #include "taler-merchant-httpd_delete-private-categories-CATEGORY_ID.h"
     32 #include "taler-merchant-httpd_delete-private-units-UNIT.h"
     33 #include "taler-merchant-httpd_delete-management-instances-INSTANCE.h"
     34 #include "taler-merchant-httpd_delete-private-token.h"
     35 #include "taler-merchant-httpd_delete-private-tokens-SERIAL.h"
     36 #include "taler-merchant-httpd_delete-private-products-PRODUCT_ID.h"
     37 #include "taler-merchant-httpd_delete-private-orders-ORDER_ID.h"
     38 #include "taler-merchant-httpd_delete-private-fountains-FOUNTAIN_ID.h"
     39 #include "taler-merchant-httpd_delete-private-otp-devices-DEVICE_ID.h"
     40 #include "taler-merchant-httpd_delete-private-templates-TEMPLATE_ID.h"
     41 #include "taler-merchant-httpd_delete-private-tokenfamilies-TOKEN_FAMILY_SLUG.h"
     42 #include "taler-merchant-httpd_delete-private-transfers-TID.h"
     43 #include "taler-merchant-httpd_delete-private-webhooks-WEBHOOK_ID.h"
     44 #include "taler-merchant-httpd_get-private-accounts.h"
     45 #include "taler-merchant-httpd_get-private-accounts-H_WIRE.h"
     46 #include "taler-merchant-httpd_get-private-categories.h"
     47 #include "taler-merchant-httpd_get-private-categories-CATEGORY_ID.h"
     48 #include "taler-merchant-httpd_get-private-units.h"
     49 #include "taler-merchant-httpd_get-private-units-UNIT.h"
     50 #include "taler-merchant-httpd_get-private-incoming.h"
     51 #include "taler-merchant-httpd_get-private-incoming-ID.h"
     52 #include "taler-merchant-httpd_get-management-instances.h"
     53 #include "taler-merchant-httpd_get-management-instances-INSTANCE.h"
     54 #include "taler-merchant-httpd_get-private-kyc.h"
     55 #include "taler-merchant-httpd_get-private-tokens.h"
     56 #include "taler-merchant-httpd_get-private-pos.h"
     57 #include "taler-merchant-httpd_get-private-products.h"
     58 #include "taler-merchant-httpd_get-private-products-PRODUCT_ID.h"
     59 #include "taler-merchant-httpd_get-private-orders.h"
     60 #include "taler-merchant-httpd_get-private-orders-ORDER_ID.h"
     61 #include "taler-merchant-httpd_get-private-otp-devices.h"
     62 #include "taler-merchant-httpd_get-private-fountains.h"
     63 #include "taler-merchant-httpd_get-private-fountains-FOUNTAIN_ID.h"
     64 #include "taler-merchant-httpd_get-private-otp-devices-DEVICE_ID.h"
     65 #include "taler-merchant-httpd_get-private-statistics-amount-SLUG.h"
     66 #include "taler-merchant-httpd_get-private-statistics-counter-SLUG.h"
     67 #include "taler-merchant-httpd_get-private-statistics-report-transactions.h"
     68 #include "taler-merchant-httpd_get-private-templates.h"
     69 #include "taler-merchant-httpd_get-private-templates-TEMPLATE_ID.h"
     70 #include "taler-merchant-httpd_get-private-tokenfamilies.h"
     71 #include "taler-merchant-httpd_get-private-tokenfamilies-TOKEN_FAMILY_SLUG.h"
     72 #include "taler-merchant-httpd_get-private-transfers.h"
     73 #include "taler-merchant-httpd_get-private-webhooks.h"
     74 #include "taler-merchant-httpd_get-private-webhooks-WEBHOOK_ID.h"
     75 #include "taler-merchant-httpd_patch-private-accounts-H_WIRE.h"
     76 #include "taler-merchant-httpd_patch-private-categories-CATEGORY_ID.h"
     77 #include "taler-merchant-httpd_patch-private-units-UNIT.h"
     78 #include "taler-merchant-httpd_patch-management-instances-INSTANCE.h"
     79 #include "taler-merchant-httpd_patch-private-orders-ORDER_ID-forget.h"
     80 #include "taler-merchant-httpd_patch-private-fountains-FOUNTAIN_ID.h"
     81 #include "taler-merchant-httpd_patch-private-otp-devices-DEVICE_ID.h"
     82 #include "taler-merchant-httpd_patch-private-products-PRODUCT_ID.h"
     83 #include "taler-merchant-httpd_patch-private-templates-TEMPLATE_ID.h"
     84 #include "taler-merchant-httpd_patch-private-tokenfamilies-TOKEN_FAMILY_SLUG.h"
     85 #include "taler-merchant-httpd_patch-private-webhooks-WEBHOOK_ID.h"
     86 #include "taler-merchant-httpd_post-private-accounts.h"
     87 #include "taler-merchant-httpd_post-private-categories.h"
     88 #include "taler-merchant-httpd_post-private-units.h"
     89 #include "taler-merchant-httpd_post-management-instances.h"
     90 #include "taler-merchant-httpd_post-management-instances-INSTANCE-auth.h"
     91 #include "taler-merchant-httpd_post-private-token.h"
     92 #include "taler-merchant-httpd_post-private-fountains.h"
     93 #include "taler-merchant-httpd_post-private-otp-devices.h"
     94 #include "taler-merchant-httpd_post-private-orders.h"
     95 #include "taler-merchant-httpd_post-private-orders-ORDER_ID-collect.h"
     96 #include "taler-merchant-httpd_post-private-orders-ORDER_ID-refund.h"
     97 #include "taler-merchant-httpd_post-private-orders-ORDER_ID-refund-external.h"
     98 #include "taler-merchant-httpd_post-private-products.h"
     99 #include "taler-merchant-httpd_post-private-products-PRODUCT_ID-lock.h"
    100 #include "taler-merchant-httpd_post-private-templates.h"
    101 #include "taler-merchant-httpd_post-private-tokenfamilies.h"
    102 #include "taler-merchant-httpd_post-private-transfers.h"
    103 #include "taler-merchant-httpd_post-private-webhooks.h"
    104 #include "taler-merchant-httpd_post-private-accounts-H_WIRE-kycauth.h"
    105 #include "taler-merchant-httpd_post-private-accept-tos-early.h"
    106 #include "taler-merchant-httpd_post-challenge-ID.h"
    107 #include "taler-merchant-httpd_post-challenge-ID-confirm.h"
    108 #include "taler-merchant-httpd_post-orders-ORDER_ID-abort.h"
    109 #include "taler-merchant-httpd_post-orders-ORDER_ID-claim.h"
    110 #include "taler-merchant-httpd_post-orders-ORDER_ID-paid.h"
    111 #include "taler-merchant-httpd_get-fountain-info.h"
    112 #include "taler-merchant-httpd_post-fountain-withdraw.h"
    113 #include "taler-merchant-httpd_post-orders-ORDER_ID-pay.h"
    114 #include "taler-merchant-httpd_post-orders-ORDER_ID-unclaim.h"
    115 #include "taler-merchant-httpd_post-templates-TEMPLATE_ID.h"
    116 #include "taler-merchant-httpd_post-orders-ORDER_ID-refund.h"
    117 #include "taler-merchant-httpd_get-webui.h"
    118 #include "taler-merchant-httpd_statics.h"
    119 #include "taler-merchant-httpd_get-terms.h"
    120 #include "taler-merchant-httpd_post-reports-REPORT_ID.h"
    121 #include "taler-merchant-httpd_delete-private-reports-REPORT_ID.h"
    122 #include "taler-merchant-httpd_get-private-reports-REPORT_ID.h"
    123 #include "taler-merchant-httpd_get-private-reports.h"
    124 #include "taler-merchant-httpd_patch-private-reports-REPORT_ID.h"
    125 #include "taler-merchant-httpd_post-private-reports.h"
    126 #include "taler-merchant-httpd_delete-private-pots-POT_ID.h"
    127 #include "taler-merchant-httpd_get-private-pots-POT_ID.h"
    128 #include "taler-merchant-httpd_get-private-pots.h"
    129 #include "taler-merchant-httpd_patch-private-pots-POT_ID.h"
    130 #include "taler-merchant-httpd_post-private-pots.h"
    131 #include "taler-merchant-httpd_get-private-groups.h"
    132 #include "taler-merchant-httpd_post-private-groups.h"
    133 #include "taler-merchant-httpd_patch-private-groups-GROUP_ID.h"
    134 #include "taler-merchant-httpd_delete-private-groups-GROUP_ID.h"
    135 #include "taler-merchant-httpd_get-private-donau.h"
    136 #include "taler-merchant-httpd_post-private-donau.h"
    137 #include "taler-merchant-httpd_delete-private-donau-DONAU_SERIAL.h"
    138 
    139 
    140 /**
    141  * Handle a OPTIONS "*" request.
    142  *
    143  * @param rh context of the handler
    144  * @param connection the MHD connection to handle
    145  * @param[in,out] hc context with further information about the request
    146  * @return MHD result code
    147  */
    148 static enum MHD_Result
    149 handle_server_options (const struct TMH_RequestHandler *rh,
    150                        struct MHD_Connection *connection,
    151                        struct TMH_HandlerContext *hc)
    152 {
    153   (void) rh;
    154   (void) hc;
    155   return TALER_MHD_reply_cors_preflight (connection);
    156 }
    157 
    158 
    159 /**
    160  * Generates the response for "/", redirecting the
    161  * client to the "/webui/" from where we serve the SPA.
    162  *
    163  * @param rh request handler
    164  * @param connection MHD connection
    165  * @param hc handler context
    166  * @return MHD result code
    167  */
    168 static enum MHD_Result
    169 spa_redirect (const struct TMH_RequestHandler *rh,
    170               struct MHD_Connection *connection,
    171               struct TMH_HandlerContext *hc)
    172 {
    173   const char *text = "Redirecting to /webui/";
    174   struct MHD_Response *response;
    175   char *dst;
    176 
    177   response = MHD_create_response_from_buffer (strlen (text),
    178                                               (void *) text,
    179                                               MHD_RESPMEM_PERSISTENT);
    180   if (NULL == response)
    181   {
    182     GNUNET_break (0);
    183     return MHD_NO;
    184   }
    185   TALER_MHD_add_global_headers (response,
    186                                 true);
    187   GNUNET_break (MHD_YES ==
    188                 MHD_add_response_header (response,
    189                                          MHD_HTTP_HEADER_CONTENT_TYPE,
    190                                          "text/plain"));
    191   if ( (NULL == hc->instance) ||
    192        (0 == strcmp ("admin",
    193                      hc->instance->settings.id)) )
    194     dst = GNUNET_strdup ("/webui/");
    195   else
    196     GNUNET_asprintf (&dst,
    197                      "/instances/%s/webui/",
    198                      hc->instance->settings.id);
    199   if (MHD_NO ==
    200       MHD_add_response_header (response,
    201                                MHD_HTTP_HEADER_LOCATION,
    202                                dst))
    203   {
    204     GNUNET_break (0);
    205     MHD_destroy_response (response);
    206     GNUNET_free (dst);
    207     return MHD_NO;
    208   }
    209   GNUNET_free (dst);
    210 
    211   {
    212     enum MHD_Result ret;
    213 
    214     ret = MHD_queue_response (connection,
    215                               MHD_HTTP_FOUND,
    216                               response);
    217     MHD_destroy_response (response);
    218     return ret;
    219   }
    220 }
    221 
    222 
    223 /**
    224  * Determine the group of request handlers to call for the
    225  * given URL. Removes a possible prefix from @a purl by advancing
    226  * the pointer.
    227  *
    228  * @param[in,out] urlp pointer to the URL to analyze and update
    229  * @param[out] is_public set to true if these are public handlers
    230  * @return handler group to consider for the given URL
    231  */
    232 static const struct TMH_RequestHandler *
    233 determine_handler_group (const char **urlp,
    234                          bool *is_public)
    235 {
    236   static struct TMH_RequestHandler management_handlers[] = {
    237     /* GET /instances */
    238     {
    239       .url_prefix = "/instances",
    240       .method = MHD_HTTP_METHOD_GET,
    241       .permission = "instances-write",
    242       .skip_instance = true,
    243       .default_only = true,
    244       .handler = &TMH_private_get_instances
    245     },
    246     /* POST /instances */
    247     {
    248       .url_prefix = "/instances",
    249       .method = MHD_HTTP_METHOD_POST,
    250       .permission = "instances-write",
    251       .skip_instance = true,
    252       .default_only = true,
    253       .handler = &TMH_private_post_instances,
    254       /* allow instance data of up to 8 MB, that should be plenty;
    255          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    256          would require further changes to the allocation logic
    257          in the code... */
    258       .max_upload = 1024 * 1024 * 8
    259     },
    260     /* GET /instances/$ID/ */
    261     {
    262       .url_prefix = "/instances/",
    263       .method = MHD_HTTP_METHOD_GET,
    264       .permission = "instances-write",
    265       .skip_instance = true,
    266       .default_only = true,
    267       .have_id_segment = true,
    268       .handler = &TMH_private_get_instances_default_ID
    269     },
    270     /* DELETE /instances/$ID */
    271     {
    272       .url_prefix = "/instances/",
    273       .method = MHD_HTTP_METHOD_DELETE,
    274       .permission = "instances-write",
    275       .skip_instance = true,
    276       .default_only = true,
    277       .have_id_segment = true,
    278       .handler = &TMH_private_delete_instances_default_ID
    279     },
    280     /* PATCH /instances/$ID */
    281     {
    282       .url_prefix = "/instances/",
    283       .method = MHD_HTTP_METHOD_PATCH,
    284       .permission = "instances-write",
    285       .skip_instance = true,
    286       .default_only = true,
    287       .have_id_segment = true,
    288       .handler = &TMH_private_patch_instances_default_ID,
    289       /* allow instance data of up to 8 MB, that should be plenty;
    290          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    291          would require further changes to the allocation logic
    292          in the code... */
    293       .max_upload = 1024 * 1024 * 8
    294     },
    295     /* POST /auth: */
    296     {
    297       .url_prefix = "/instances/",
    298       .url_suffix = "auth",
    299       .method = MHD_HTTP_METHOD_POST,
    300       .permission = "instances-auth-write",
    301       .skip_instance = true,
    302       .default_only = true,
    303       .have_id_segment = true,
    304       .handler = &TMH_private_post_instances_default_ID_auth,
    305       /* Body should be pretty small. */
    306       .max_upload = 1024 * 1024
    307     },
    308     /* GET /kyc: */
    309     {
    310       .url_prefix = "/instances/",
    311       .url_suffix = "kyc",
    312       .method = MHD_HTTP_METHOD_GET,
    313       .permission = "instances-kyc-read",
    314       .skip_instance = true,
    315       .default_only = true,
    316       .have_id_segment = true,
    317       .handler = &TMH_private_get_instances_default_ID_kyc,
    318     },
    319     {
    320       .url_prefix = NULL
    321     }
    322   };
    323 
    324   static struct TMH_RequestHandler private_handlers[] = {
    325     /* GET /instances/$ID/: */
    326     {
    327       .url_prefix = "/",
    328       .method = MHD_HTTP_METHOD_GET,
    329       .permission = "instances-read",
    330       .handler = &TMH_private_get_instances_ID
    331     },
    332     /* DELETE /instances/$ID/: */
    333     {
    334       .url_prefix = "/",
    335       .method = MHD_HTTP_METHOD_DELETE,
    336       .permission = "instances-write",
    337       .allow_deleted_instance = true,
    338       .handler = &TMH_private_delete_instances_ID
    339     },
    340     /* PATCH /instances/$ID/: */
    341     {
    342       .url_prefix = "/",
    343       .method = MHD_HTTP_METHOD_PATCH,
    344       .handler = &TMH_private_patch_instances_ID,
    345       .permission = "instances-write",
    346       .allow_deleted_instance = true,
    347       /* allow instance data of up to 8 MB, that should be plenty;
    348          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    349          would require further changes to the allocation logic
    350          in the code... */
    351       .max_upload = 1024 * 1024 * 8
    352     },
    353     /* POST /auth: */
    354     {
    355       .url_prefix = "/auth",
    356       .method = MHD_HTTP_METHOD_POST,
    357       .handler = &TMH_private_post_instances_ID_auth,
    358       .permission = "auth-write",
    359       /* Body should be pretty small. */
    360       .max_upload = 1024 * 1024,
    361     },
    362     /* GET /kyc: */
    363     {
    364       .url_prefix = "/kyc",
    365       .method = MHD_HTTP_METHOD_GET,
    366       .permission = "kyc-read",
    367       .handler = &TMH_private_get_instances_ID_kyc,
    368     },
    369     /* GET /pos: */
    370     {
    371       .url_prefix = "/pos",
    372       .method = MHD_HTTP_METHOD_GET,
    373       .permission = "pos-read",
    374       .handler = &TMH_private_get_pos
    375     },
    376     /* GET /categories: */
    377     {
    378       .url_prefix = "/categories",
    379       .method = MHD_HTTP_METHOD_GET,
    380       .permission = "categories-read",
    381       .handler = &TMH_private_get_categories
    382     },
    383     /* POST /categories: */
    384     {
    385       .url_prefix = "/categories",
    386       .method = MHD_HTTP_METHOD_POST,
    387       .permission = "categories-write",
    388       .handler = &TMH_private_post_categories,
    389       /* allow category data of up to 8 kb, that should be plenty */
    390       .max_upload = 1024 * 8
    391     },
    392     /* GET /categories/$ID: */
    393     {
    394       .url_prefix = "/categories/",
    395       .method = MHD_HTTP_METHOD_GET,
    396       .permission = "categories-read",
    397       .have_id_segment = true,
    398       .allow_deleted_instance = true,
    399       .handler = &TMH_private_get_categories_ID
    400     },
    401     /* DELETE /categories/$ID: */
    402     {
    403       .url_prefix = "/categories/",
    404       .method = MHD_HTTP_METHOD_DELETE,
    405       .permission = "categories-write",
    406       .have_id_segment = true,
    407       .allow_deleted_instance = true,
    408       .handler = &TMH_private_delete_categories_ID
    409     },
    410     /* PATCH /categories/$ID/: */
    411     {
    412       .url_prefix = "/categories/",
    413       .method = MHD_HTTP_METHOD_PATCH,
    414       .permission = "categories-write",
    415       .have_id_segment = true,
    416       .allow_deleted_instance = true,
    417       .handler = &TMH_private_patch_categories_ID,
    418       /* allow category data of up to 8 kb, that should be plenty */
    419       .max_upload = 1024 * 8
    420     },
    421     /* GET /units: */
    422     {
    423       .url_prefix = "/units",
    424       .method = MHD_HTTP_METHOD_GET,
    425       .permission = "units-read",
    426       .handler = &TMH_private_get_units
    427     },
    428     /* POST /units: */
    429     {
    430       .url_prefix = "/units",
    431       .method = MHD_HTTP_METHOD_POST,
    432       .permission = "units-write",
    433       .handler = &TMH_private_post_units,
    434       .max_upload = 1024 * 8
    435     },
    436     /* GET /units/$UNIT: */
    437     {
    438       .url_prefix = "/units/",
    439       .method = MHD_HTTP_METHOD_GET,
    440       .have_id_segment = true,
    441       .allow_deleted_instance = true,
    442       .permission = "units-read",
    443       .handler = &TMH_private_get_units_ID
    444     },
    445     /* DELETE /units/$UNIT: */
    446     {
    447       .url_prefix = "/units/",
    448       .method = MHD_HTTP_METHOD_DELETE,
    449       .permission = "units-write",
    450       .have_id_segment = true,
    451       .allow_deleted_instance = true,
    452       .handler = &TMH_private_delete_units_ID
    453     },
    454     /* PATCH /units/$UNIT: */
    455     {
    456       .url_prefix = "/units/",
    457       .method = MHD_HTTP_METHOD_PATCH,
    458       .permission = "units-write",
    459       .have_id_segment = true,
    460       .allow_deleted_instance = true,
    461       .handler = &TMH_private_patch_units_ID,
    462       .max_upload = 1024 * 8
    463     },
    464     /* GET /products: */
    465     {
    466       .url_prefix = "/products",
    467       .permission = "products-read",
    468       .method = MHD_HTTP_METHOD_GET,
    469       .handler = &TMH_private_get_products
    470     },
    471     /* POST /products: */
    472     {
    473       .url_prefix = "/products",
    474       .method = MHD_HTTP_METHOD_POST,
    475       .permission = "products-write",
    476       .handler = &TMH_private_post_products,
    477       /* allow product data of up to 8 MB, that should be plenty;
    478          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    479          would require further changes to the allocation logic
    480          in the code... */
    481       .max_upload = 1024 * 1024 * 8
    482     },
    483     /* GET /products/$ID: */
    484     {
    485       .url_prefix = "/products/",
    486       .method = MHD_HTTP_METHOD_GET,
    487       .have_id_segment = true,
    488       .permission = "products-read",
    489       .allow_deleted_instance = true,
    490       .handler = &TMH_private_get_products_ID
    491     },
    492     /* DELETE /products/$ID/: */
    493     {
    494       .url_prefix = "/products/",
    495       .method = MHD_HTTP_METHOD_DELETE,
    496       .have_id_segment = true,
    497       .permission = "products-write",
    498       .allow_deleted_instance = true,
    499       .handler = &TMH_private_delete_products_ID
    500     },
    501     /* PATCH /products/$ID/: */
    502     {
    503       .url_prefix = "/products/",
    504       .method = MHD_HTTP_METHOD_PATCH,
    505       .have_id_segment = true,
    506       .allow_deleted_instance = true,
    507       .permission = "products-write",
    508       .handler = &TMH_private_patch_products_ID,
    509       /* allow product data of up to 8 MB, that should be plenty;
    510          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    511          would require further changes to the allocation logic
    512          in the code... */
    513       .max_upload = 1024 * 1024 * 8
    514     },
    515     /* POST /products/$ID/lock: */
    516     {
    517       .url_prefix = "/products/",
    518       .url_suffix = "lock",
    519       .method = MHD_HTTP_METHOD_POST,
    520       .have_id_segment = true,
    521       .permission = "products-lock",
    522       .handler = &TMH_private_post_products_ID_lock,
    523       /* the body should be pretty small, allow 1 MB of upload
    524          to set a conservative bound for sane wallets */
    525       .max_upload = 1024 * 1024
    526     },
    527     /* POST /orders: */
    528     {
    529       .url_prefix = "/orders",
    530       .method = MHD_HTTP_METHOD_POST,
    531       .permission = "orders-write",
    532       .handler = &TMH_private_post_orders,
    533       /* allow contracts of up to 8 MB, that should be plenty;
    534          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    535          would require further changes to the allocation logic
    536          in the code... */
    537       .max_upload = 1024 * 1024 * 8
    538     },
    539     /* GET /orders/$ID: */
    540     {
    541       .url_prefix = "/orders/",
    542       .method = MHD_HTTP_METHOD_GET,
    543       .permission = "orders-read",
    544       .have_id_segment = true,
    545       .allow_deleted_instance = true,
    546       .handler = &TMH_private_get_orders_ID
    547     },
    548     /* GET /orders: */
    549     {
    550       .url_prefix = "/orders",
    551       .method = MHD_HTTP_METHOD_GET,
    552       .permission = "orders-read",
    553       .allow_deleted_instance = true,
    554       .handler = &TMH_private_get_orders
    555     },
    556     /* POST /orders/$ID/refund: */
    557     {
    558       .url_prefix = "/orders/",
    559       .url_suffix = "refund",
    560       .method = MHD_HTTP_METHOD_POST,
    561       .have_id_segment = true,
    562       .permission = "orders-refund",
    563       .handler = &TMH_private_post_orders_ID_refund,
    564       /* the body should be pretty small, allow 1 MB of upload
    565          to set a conservative bound for sane wallets */
    566       .max_upload = 1024 * 1024
    567     },
    568     /* POST /orders/$ID/refund-external: */
    569     {
    570       .url_prefix = "/orders/",
    571       .url_suffix = "refund-external",
    572       .method = MHD_HTTP_METHOD_POST,
    573       .have_id_segment = true,
    574       .permission = "orders-refund",
    575       .handler = &TMH_private_post_orders_ID_refund_external,
    576       /* the body should be pretty small, allow 1 MB of upload
    577          to set a conservative bound for sane wallets */
    578       .max_upload = 1024 * 1024
    579     },
    580     /* POST /orders/$ID/collect: */
    581     {
    582       .url_prefix = "/orders/",
    583       .url_suffix = "collect",
    584       .method = MHD_HTTP_METHOD_POST,
    585       .have_id_segment = true,
    586       .permission = "orders-write",
    587       .handler = &TMH_private_post_orders_ID_collect,
    588       /* the body should be pretty small, allow 1 MB of upload
    589          to set a conservative bound for sane wallets */
    590       .max_upload = 1024 * 1024
    591     },
    592     /* PATCH /orders/$ID/forget: */
    593     {
    594       .url_prefix = "/orders/",
    595       .url_suffix = "forget",
    596       .method = MHD_HTTP_METHOD_PATCH,
    597       .permission = "orders-write",
    598       .have_id_segment = true,
    599       .allow_deleted_instance = true,
    600       .handler = &TMH_private_patch_orders_ID_forget,
    601       /* the body should be pretty small, allow 1 MB of upload
    602          to set a conservative bound for sane wallets */
    603       .max_upload = 1024 * 1024
    604     },
    605     /* DELETE /orders/$ID: */
    606     {
    607       .url_prefix = "/orders/",
    608       .method = MHD_HTTP_METHOD_DELETE,
    609       .permission = "orders-write",
    610       .have_id_segment = true,
    611       .allow_deleted_instance = true,
    612       .handler = &TMH_private_delete_orders_ID
    613     },
    614     /* POST /transfers: */
    615     {
    616       .url_prefix = "/transfers",
    617       .method = MHD_HTTP_METHOD_POST,
    618       .allow_deleted_instance = true,
    619       .handler = &TMH_private_post_transfers,
    620       .permission = "transfers-write",
    621       /* the body should be pretty small, allow 1 MB of upload
    622          to set a conservative bound for sane wallets */
    623       .max_upload = 1024 * 1024
    624     },
    625     /* DELETE /transfers/$ID: */
    626     {
    627       .url_prefix = "/transfers/",
    628       .method = MHD_HTTP_METHOD_DELETE,
    629       .permission = "transfers-write",
    630       .allow_deleted_instance = true,
    631       .handler = &TMH_private_delete_transfers_ID,
    632       .have_id_segment = true,
    633       /* the body should be pretty small, allow 1 MB of upload
    634          to set a conservative bound for sane wallets */
    635       .max_upload = 1024 * 1024
    636     },
    637     /* GET /transfers: */
    638     {
    639       .url_prefix = "/transfers",
    640       .permission = "transfers-read",
    641       .method = MHD_HTTP_METHOD_GET,
    642       .allow_deleted_instance = true,
    643       .handler = &TMH_private_get_transfers
    644     },
    645     /* GET /incoming: */
    646     {
    647       .url_prefix = "/incoming",
    648       .permission = "transfers-read",
    649       .method = MHD_HTTP_METHOD_GET,
    650       .allow_deleted_instance = true,
    651       .handler = &TMH_private_get_incoming
    652     },
    653     /* GET /incoming/$ID: */
    654     {
    655       .url_prefix = "/incoming/",
    656       .permission = "transfers-read",
    657       .method = MHD_HTTP_METHOD_GET,
    658       .allow_deleted_instance = true,
    659       .have_id_segment = true,
    660       .handler = &TMH_private_get_incoming_ID
    661     },
    662     /* POST /fountains: */
    663     {
    664       .url_prefix = "/fountains",
    665       .permission = "fountains-write",
    666       .method = MHD_HTTP_METHOD_POST,
    667       .handler = &TMH_private_post_fountains
    668     },
    669     /* GET /fountains: */
    670     {
    671       .url_prefix = "/fountains",
    672       .permission = "fountains-read",
    673       .method = MHD_HTTP_METHOD_GET,
    674       .handler = &TMH_private_get_fountains
    675     },
    676     /* GET /fountains/$ID: */
    677     {
    678       .url_prefix = "/fountains/",
    679       .permission = "fountains-read",
    680       .method = MHD_HTTP_METHOD_GET,
    681       .have_id_segment = true,
    682       .handler = &TMH_private_get_fountains_FOUNTAIN_ID
    683     },
    684     /* PATCH /fountains/$ID: */
    685     {
    686       .url_prefix = "/fountains/",
    687       .permission = "fountains-write",
    688       .method = MHD_HTTP_METHOD_PATCH,
    689       .have_id_segment = true,
    690       .handler = &TMH_private_patch_fountains_FOUNTAIN_ID
    691     },
    692     /* DELETE /fountains/$ID: */
    693     {
    694       .url_prefix = "/fountains/",
    695       .permission = "fountains-write",
    696       .method = MHD_HTTP_METHOD_DELETE,
    697       .have_id_segment = true,
    698       .handler = &TMH_private_delete_fountains_FOUNTAIN_ID
    699     },
    700     /* POST /otp-devices: */
    701     {
    702       .url_prefix = "/otp-devices",
    703       .permission = "otp-devices-write",
    704       .method = MHD_HTTP_METHOD_POST,
    705       .handler = &TMH_private_post_otp_devices
    706     },
    707     /* GET /otp-devices: */
    708     {
    709       .url_prefix = "/otp-devices",
    710       .permission = "otp-devices-read",
    711       .method = MHD_HTTP_METHOD_GET,
    712       .handler = &TMH_private_get_otp_devices
    713     },
    714     /* GET /otp-devices/$ID: */
    715     {
    716       .url_prefix = "/otp-devices/",
    717       .method = MHD_HTTP_METHOD_GET,
    718       .permission = "otp-devices-read",
    719       .have_id_segment = true,
    720       .handler = &TMH_private_get_otp_devices_ID
    721     },
    722     /* DELETE /otp-devices/$ID: */
    723     {
    724       .url_prefix = "/otp-devices/",
    725       .method = MHD_HTTP_METHOD_DELETE,
    726       .permission = "otp-devices-write",
    727       .have_id_segment = true,
    728       .handler = &TMH_private_delete_otp_devices_ID
    729     },
    730     /* PATCH /otp-devices/$ID: */
    731     {
    732       .url_prefix = "/otp-devices/",
    733       .method = MHD_HTTP_METHOD_PATCH,
    734       .permission = "otp-devices-write",
    735       .have_id_segment = true,
    736       .handler = &TMH_private_patch_otp_devices_ID
    737     },
    738     /* POST /templates: */
    739     {
    740       .url_prefix = "/templates",
    741       .method = MHD_HTTP_METHOD_POST,
    742       .permission = "templates-write",
    743       .handler = &TMH_private_post_templates,
    744       /* allow template data of up to 8 MB, that should be plenty;
    745          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    746          would require further changes to the allocation logic
    747          in the code... */
    748       .max_upload = 1024 * 1024 * 8
    749     },
    750     /* GET /templates: */
    751     {
    752       .url_prefix = "/templates",
    753       .permission = "templates-read",
    754       .method = MHD_HTTP_METHOD_GET,
    755       .handler = &TMH_private_get_templates
    756     },
    757     /* GET /templates/$ID/: */
    758     {
    759       .url_prefix = "/templates/",
    760       .method = MHD_HTTP_METHOD_GET,
    761       .permission = "templates-read",
    762       .have_id_segment = true,
    763       .allow_deleted_instance = true,
    764       .handler = &TMH_private_get_templates_ID
    765     },
    766     /* DELETE /templates/$ID/: */
    767     {
    768       .url_prefix = "/templates/",
    769       .method = MHD_HTTP_METHOD_DELETE,
    770       .permission = "templates-write",
    771       .have_id_segment = true,
    772       .allow_deleted_instance = true,
    773       .handler = &TMH_private_delete_templates_ID
    774     },
    775     /* PATCH /templates/$ID/: */
    776     {
    777       .url_prefix = "/templates/",
    778       .method = MHD_HTTP_METHOD_PATCH,
    779       .permission = "templates-write",
    780       .have_id_segment = true,
    781       .allow_deleted_instance = true,
    782       .handler = &TMH_private_patch_templates_ID,
    783       /* allow template data of up to 8 MB, that should be plenty;
    784          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    785          would require further changes to the allocation logic
    786          in the code... */
    787       .max_upload = 1024 * 1024 * 8
    788     },
    789 
    790     /* POST /pots: */
    791     {
    792       .url_prefix = "/pots",
    793       .method = MHD_HTTP_METHOD_POST,
    794       .permission = "pots-write",
    795       .handler = &TMH_private_post_pots,
    796     },
    797     /* GET /pots: */
    798     {
    799       .url_prefix = "/pots",
    800       .permission = "pots-read",
    801       .method = MHD_HTTP_METHOD_GET,
    802       .handler = &TMH_private_get_pots
    803     },
    804     /* DELETE /pots/$ID: */
    805     {
    806       .url_prefix = "/pots/",
    807       .method = MHD_HTTP_METHOD_DELETE,
    808       .permission = "pots-write",
    809       .have_id_segment = true,
    810       .handler = &TMH_private_delete_pot
    811     },
    812     /* PATCH /pots/$ID: */
    813     {
    814       .url_prefix = "/pots/",
    815       .method = MHD_HTTP_METHOD_PATCH,
    816       .permission = "pots-write",
    817       .have_id_segment = true,
    818       .handler = &TMH_private_patch_pot,
    819     },
    820 
    821     /* GET /webhooks: */
    822     {
    823       .url_prefix = "/webhooks",
    824       .permission = "webhooks-read",
    825       .method = MHD_HTTP_METHOD_GET,
    826       .handler = &TMH_private_get_webhooks
    827     },
    828     /* POST /webhooks: */
    829     {
    830       .url_prefix = "/webhooks",
    831       .method = MHD_HTTP_METHOD_POST,
    832       .permission = "webhooks-write",
    833       .handler = &TMH_private_post_webhooks,
    834       /* allow webhook data of up to 8 MB, that should be plenty;
    835          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    836          would require further changes to the allocation logic
    837          in the code... */
    838       .max_upload = 1024 * 1024 * 8
    839     },
    840     /* GET /webhooks/$ID/: */
    841     {
    842       .url_prefix = "/webhooks/",
    843       .method = MHD_HTTP_METHOD_GET,
    844       .permission = "webhooks-read",
    845       .have_id_segment = true,
    846       .allow_deleted_instance = true,
    847       .handler = &TMH_private_get_webhooks_ID
    848     },
    849     /* DELETE /webhooks/$ID/: */
    850     {
    851       .url_prefix = "/webhooks/",
    852       .permission = "webhooks-write",
    853       .method = MHD_HTTP_METHOD_DELETE,
    854       .have_id_segment = true,
    855       .allow_deleted_instance = true,
    856       .handler = &TMH_private_delete_webhooks_ID
    857     },
    858     /* PATCH /webhooks/$ID/: */
    859     {
    860       .url_prefix = "/webhooks/",
    861       .method = MHD_HTTP_METHOD_PATCH,
    862       .permission = "webhooks-write",
    863       .have_id_segment = true,
    864       .allow_deleted_instance = true,
    865       .handler = &TMH_private_patch_webhooks_ID,
    866       /* allow webhook data of up to 8 MB, that should be plenty;
    867          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    868          would require further changes to the allocation logic
    869          in the code... */
    870       .max_upload = 1024 * 1024 * 8
    871     },
    872     /* POST /accounts: */
    873     {
    874       .url_prefix = "/accounts",
    875       .method = MHD_HTTP_METHOD_POST,
    876       .permission = "accounts-write",
    877       .handler = &TMH_private_post_account,
    878       /* allow account details of up to 8 kb, that should be plenty */
    879       .max_upload = 1024 * 8
    880     },
    881     /* POST /accounts/H_WIRE/kycauth: */
    882     {
    883       .url_prefix = "/accounts/",
    884       .url_suffix = "kycauth",
    885       .method = MHD_HTTP_METHOD_POST,
    886       .have_id_segment = true,
    887       .permission = "accounts-read",
    888       .handler = &TMH_private_post_accounts_H_WIRE_kycauth,
    889       /* allow exchange URL up to 4 kb, that should be plenty */
    890       .max_upload = 1024 * 4
    891     },
    892     /* POST /accept-tos-early: */
    893     {
    894       .url_prefix = "/accept-tos-early",
    895       .method = MHD_HTTP_METHOD_POST,
    896       .permission = "accounts-write",
    897       .handler = &TMH_private_post_accept_tos_early,
    898       /* allow exchange URL plus terms version up to 4 kb */
    899       .max_upload = 1024 * 4
    900     },
    901     /* PATCH /accounts/$H_WIRE: */
    902     {
    903       .url_prefix = "/accounts/",
    904       .method = MHD_HTTP_METHOD_PATCH,
    905       .permission = "accounts-write",
    906       .handler = &TMH_private_patch_accounts_ID,
    907       .have_id_segment = true,
    908       /* allow account details of up to 8 kb, that should be plenty */
    909       .max_upload = 1024 * 8
    910     },
    911     /* GET /accounts: */
    912     {
    913       .url_prefix = "/accounts",
    914       .permission = "accounts-read",
    915       .method = MHD_HTTP_METHOD_GET,
    916       .handler = &TMH_private_get_accounts
    917     },
    918     /* GET /accounts/$H_WIRE: */
    919     {
    920       .url_prefix = "/accounts/",
    921       .permission = "accounts-read",
    922       .method = MHD_HTTP_METHOD_GET,
    923       .have_id_segment = true,
    924       .handler = &TMH_private_get_accounts_ID
    925     },
    926     /* DELETE /accounts/$H_WIRE: */
    927     {
    928       .url_prefix = "/accounts/",
    929       .permission = "accounts-write",
    930       .method = MHD_HTTP_METHOD_DELETE,
    931       .handler = &TMH_private_delete_account_ID,
    932       .have_id_segment = true
    933     },
    934     /* GET /tokens: */
    935     {
    936       .url_prefix = "/tokens",
    937       .permission = "tokens-read",
    938       .method = MHD_HTTP_METHOD_GET,
    939       .handler = &TMH_private_get_instances_ID_tokens,
    940     },
    941     /* POST /token: */
    942     {
    943       .url_prefix = "/token",
    944       .permission = "token-refresh",
    945       .method = MHD_HTTP_METHOD_POST,
    946       .handler = &TMH_private_post_instances_ID_token,
    947       /* Body should be tiny. */
    948       .max_upload = 1024
    949     },
    950     /* DELETE /tokens/$SERIAL: */
    951     {
    952       .url_prefix = "/tokens/",
    953       .permission = "tokens-write",
    954       .method = MHD_HTTP_METHOD_DELETE,
    955       .handler = &TMH_private_delete_instances_ID_token_SERIAL,
    956       .have_id_segment = true
    957     },
    958     /* DELETE /token: */
    959     {
    960       .url_prefix = "/token",
    961       .method = MHD_HTTP_METHOD_DELETE,
    962       .handler = &TMH_private_delete_instances_ID_token,
    963       .permission = NULL /* No ACL: anyone can delete any token they have */
    964     },
    965     /* GET /tokenfamilies: */
    966     {
    967       .url_prefix = "/tokenfamilies",
    968       .permission = "tokenfamilies-read",
    969       .method = MHD_HTTP_METHOD_GET,
    970       .handler = &TMH_private_get_tokenfamilies
    971     },
    972     /* POST /tokenfamilies: */
    973     {
    974       .url_prefix = "/tokenfamilies",
    975       .permission = "tokenfamilies-write",
    976       .method = MHD_HTTP_METHOD_POST,
    977       .handler = &TMH_private_post_token_families
    978     },
    979     /* GET /tokenfamilies/$SLUG/: */
    980     {
    981       .url_prefix = "/tokenfamilies/",
    982       .method = MHD_HTTP_METHOD_GET,
    983       .permission = "tokenfamilies-read",
    984       .have_id_segment = true,
    985       .handler = &TMH_private_get_tokenfamilies_SLUG
    986     },
    987     /* DELETE /tokenfamilies/$SLUG/: */
    988     {
    989       .url_prefix = "/tokenfamilies/",
    990       .method = MHD_HTTP_METHOD_DELETE,
    991       .permission = "tokenfamilies-write",
    992       .have_id_segment = true,
    993       .handler = &TMH_private_delete_token_families_SLUG
    994     },
    995     /* PATCH /tokenfamilies/$SLUG/: */
    996     {
    997       .url_prefix = "/tokenfamilies/",
    998       .method = MHD_HTTP_METHOD_PATCH,
    999       .permission = "tokenfamilies-write",
   1000       .have_id_segment = true,
   1001       .handler = &TMH_private_patch_token_family_SLUG,
   1002     },
   1003 
   1004     /* Reports endpoints */
   1005     {
   1006       .url_prefix = "/reports",
   1007       .method = MHD_HTTP_METHOD_GET,
   1008       .permission = "reports-read",
   1009       .handler = &TMH_private_get_reports,
   1010     },
   1011     {
   1012       .url_prefix = "/reports",
   1013       .method = MHD_HTTP_METHOD_POST,
   1014       .permission = "reports-write",
   1015       .handler = &TMH_private_post_reports,
   1016     },
   1017     {
   1018       .url_prefix = "/reports/",
   1019       .method = MHD_HTTP_METHOD_GET,
   1020       .handler = &TMH_private_get_report,
   1021       .permission = "reports-read",
   1022       .have_id_segment = true,
   1023     },
   1024     {
   1025       .url_prefix = "/reports/",
   1026       .method = MHD_HTTP_METHOD_PATCH,
   1027       .handler = &TMH_private_patch_report,
   1028       .permission = "reports-write",
   1029       .have_id_segment = true,
   1030     },
   1031     {
   1032       .url_prefix = "/reports/",
   1033       .method = MHD_HTTP_METHOD_DELETE,
   1034       .handler = &TMH_private_delete_report,
   1035       .permission = "reports-write",
   1036       .have_id_segment = true,
   1037     },
   1038 
   1039     /* Groups endpoints */
   1040     {
   1041       .url_prefix = "/groups",
   1042       .method = MHD_HTTP_METHOD_GET,
   1043       .permission = "groups-read",
   1044       .handler = &TMH_private_get_groups,
   1045     },
   1046     {
   1047       .url_prefix = "/groups",
   1048       .method = MHD_HTTP_METHOD_POST,
   1049       .permission = "groups-write",
   1050       .handler = &TMH_private_post_groups,
   1051     },
   1052     {
   1053       .url_prefix = "/groups/",
   1054       .method = MHD_HTTP_METHOD_PATCH,
   1055       .handler = &TMH_private_patch_group,
   1056       .permission = "groups-write",
   1057       .have_id_segment = true,
   1058     },
   1059     {
   1060       .url_prefix = "/groups/",
   1061       .method = MHD_HTTP_METHOD_DELETE,
   1062       .handler = &TMH_private_delete_group,
   1063       .permission = "groups-write",
   1064       .have_id_segment = true,
   1065     },
   1066 
   1067     /* Money pots endpoints */
   1068     {
   1069       .url_prefix = "/pots",
   1070       .method = MHD_HTTP_METHOD_GET,
   1071       .handler = &TMH_private_get_pots,
   1072       .permission = "pots-read",
   1073     },
   1074     {
   1075       .url_prefix = "/pots",
   1076       .method = MHD_HTTP_METHOD_POST,
   1077       .handler = &TMH_private_post_pots,
   1078       .permission = "pots-write"
   1079     },
   1080     {
   1081       .url_prefix = "/pots/",
   1082       .method = MHD_HTTP_METHOD_GET,
   1083       .handler = &TMH_private_get_pot,
   1084       .have_id_segment = true,
   1085       .permission =  "pots-read",
   1086     },
   1087     {
   1088       .url_prefix = "/pots/",
   1089       .method = MHD_HTTP_METHOD_PATCH,
   1090       .handler = &TMH_private_patch_pot,
   1091       .have_id_segment = true,
   1092       .permission = "pots-write"
   1093     },
   1094     {
   1095       .url_prefix = "/pots/",
   1096       .method = MHD_HTTP_METHOD_DELETE,
   1097       .handler = &TMH_private_delete_pot,
   1098       .have_id_segment = true,
   1099       .permission = "pots-write"
   1100     },
   1101 
   1102     /* GET /donau */
   1103     {
   1104       .url_prefix = "/donau",
   1105       .method = MHD_HTTP_METHOD_GET,
   1106       .permission = "donau-read",
   1107       .handler = &TMH_private_get_donau_instances
   1108     },
   1109     /* POST /donau */
   1110     {
   1111       .url_prefix = "/donau",
   1112       .method = MHD_HTTP_METHOD_POST,
   1113       .permission = "donau-write",
   1114       .handler = &TMH_private_post_donau_instance
   1115     },
   1116     /* DELETE /donau/$charity-id */
   1117     {
   1118       .url_prefix = "/donau/",
   1119       .method = MHD_HTTP_METHOD_DELETE,
   1120       .have_id_segment = true,
   1121       .permission = "donau-write",
   1122       .handler = &TMH_private_delete_donau_instance_ID
   1123     },
   1124     /* GET /statistics-counter/$SLUG: */
   1125     {
   1126       .url_prefix = "/statistics-counter/",
   1127       .method = MHD_HTTP_METHOD_GET,
   1128       .permission = "statistics-read",
   1129       .have_id_segment = true,
   1130       .handler = &TMH_private_get_statistics_counter_SLUG,
   1131     },
   1132     /* GET /statistics-amount/$SLUG: */
   1133     {
   1134       .url_prefix = "/statistics-amount/",
   1135       .method = MHD_HTTP_METHOD_GET,
   1136       .permission = "statistics-read",
   1137       .have_id_segment = true,
   1138       .handler = &TMH_private_get_statistics_amount_SLUG,
   1139     },
   1140     /* GET /statistics-report/transactions: */
   1141     {
   1142       .url_prefix = "/statistics-report/",
   1143       .url_suffix = "transactions",
   1144       .method = MHD_HTTP_METHOD_GET,
   1145       .permission = "statistics-read",
   1146       .handler = &TMH_private_get_statistics_report_transactions,
   1147     },
   1148     {
   1149       .url_prefix = NULL
   1150     }
   1151   };
   1152   static struct TMH_RequestHandler public_handlers[] = {
   1153     {
   1154       /* for "admin" instance, it does not even
   1155          have to exist before we give the WebUI */
   1156       .url_prefix = "/",
   1157       .method = MHD_HTTP_METHOD_GET,
   1158       .mime_type = "text/html",
   1159       .skip_instance = true,
   1160       .default_only = true,
   1161       .handler = &spa_redirect,
   1162       .response_code = MHD_HTTP_FOUND
   1163     },
   1164     {
   1165       .url_prefix = "/config",
   1166       .method = MHD_HTTP_METHOD_GET,
   1167       .skip_instance = true,
   1168       .default_only = true,
   1169       .handler = &MH_handler_config
   1170     },
   1171     /* GET /fountain/info (DD 98): */
   1172     {
   1173       .url_prefix = "/fountain/",
   1174       .url_suffix = "info",
   1175       .method = MHD_HTTP_METHOD_GET,
   1176       .handler = &TMH_get_fountain_info
   1177     },
   1178     /* POST /fountain/withdraw (DD 98): */
   1179     {
   1180       .url_prefix = "/fountain/",
   1181       .url_suffix = "withdraw",
   1182       .method = MHD_HTTP_METHOD_POST,
   1183       .max_upload = 1024 * 1024,
   1184       .handler = &TMH_post_fountain_withdraw
   1185     },
   1186     {
   1187       .url_prefix = "/exchanges",
   1188       .method = MHD_HTTP_METHOD_GET,
   1189       .skip_instance = true,
   1190       .default_only = true,
   1191       .handler = &MH_handler_exchanges
   1192     },
   1193     {
   1194       /* for "normal" instance,s they must exist
   1195          before we give the WebUI */
   1196       .url_prefix = "/",
   1197       .method = MHD_HTTP_METHOD_GET,
   1198       .mime_type = "text/html",
   1199       .handler = &spa_redirect,
   1200       .response_code = MHD_HTTP_FOUND
   1201     },
   1202     {
   1203       .url_prefix = "/webui/",
   1204       .method = MHD_HTTP_METHOD_GET,
   1205       .mime_type = "text/html",
   1206       .skip_instance = true,
   1207       .have_id_segment = true,
   1208       .handler = &TMH_return_spa,
   1209       .response_code = MHD_HTTP_OK
   1210     },
   1211     {
   1212       .url_prefix = "/agpl",
   1213       .method = MHD_HTTP_METHOD_GET,
   1214       .skip_instance = true,
   1215       .handler = &TMH_MHD_handler_agpl_redirect
   1216     },
   1217     {
   1218       .url_prefix = "/agpl",
   1219       .method = MHD_HTTP_METHOD_GET,
   1220       .skip_instance = true,
   1221       .handler = &TMH_MHD_handler_agpl_redirect
   1222     },
   1223     {
   1224       .url_prefix = "/terms",
   1225       .method = MHD_HTTP_METHOD_GET,
   1226       .skip_instance = true,
   1227       .handler = &TMH_handler_terms
   1228     },
   1229     {
   1230       .url_prefix = "/privacy",
   1231       .method = MHD_HTTP_METHOD_GET,
   1232       .skip_instance = true,
   1233       .handler = &TMH_handler_privacy
   1234     },
   1235     /* Also serve the same /config per instance */
   1236     {
   1237       .url_prefix = "/config",
   1238       .method = MHD_HTTP_METHOD_GET,
   1239       .handler = &MH_handler_config
   1240     },
   1241     /* POST /orders/$ID/abort: */
   1242     {
   1243       .url_prefix = "/orders/",
   1244       .have_id_segment = true,
   1245       .url_suffix = "abort",
   1246       .method = MHD_HTTP_METHOD_POST,
   1247       .handler = &TMH_post_orders_ID_abort,
   1248       /* wallet may give us many coins to sign, allow 1 MB of upload
   1249          to set a conservative bound for sane wallets */
   1250       .max_upload = 1024 * 1024
   1251     },
   1252     /* POST /orders/$ID/claim: */
   1253     {
   1254       .url_prefix = "/orders/",
   1255       .have_id_segment = true,
   1256       .url_suffix = "claim",
   1257       .method = MHD_HTTP_METHOD_POST,
   1258       .handler = &TMH_post_orders_ID_claim,
   1259       /* the body should be pretty small, allow 1 MB of upload
   1260          to set a conservative bound for sane wallets */
   1261       .max_upload = 1024 * 1024
   1262     },
   1263     /* POST /orders/$ID/unclaim: */
   1264     {
   1265       .url_prefix = "/orders/",
   1266       .have_id_segment = true,
   1267       .url_suffix = "unclaim",
   1268       .method = MHD_HTTP_METHOD_POST,
   1269       .handler = &TMH_post_orders_ID_unclaim,
   1270       /* the body should be very small */
   1271       .max_upload = 1024
   1272     },
   1273     /* POST /orders/$ID/pay: */
   1274     {
   1275       .url_prefix = "/orders/",
   1276       .have_id_segment = true,
   1277       .url_suffix = "pay",
   1278       .method = MHD_HTTP_METHOD_POST,
   1279       .handler = &TMH_post_orders_ID_pay,
   1280       /* wallet may give us many coins to sign, allow 1 MB of upload
   1281          to set a conservative bound for sane wallets */
   1282       .max_upload = 1024 * 1024
   1283     },
   1284     /* POST /orders/$ID/paid: */
   1285     {
   1286       .url_prefix = "/orders/",
   1287       .have_id_segment = true,
   1288       .allow_deleted_instance = true,
   1289       .url_suffix = "paid",
   1290       .method = MHD_HTTP_METHOD_POST,
   1291       .handler = &TMH_post_orders_ID_paid,
   1292       /* the body should be pretty small, allow 1 MB of upload
   1293          to set a conservative bound for sane wallets */
   1294       .max_upload = 1024 * 1024
   1295     },
   1296     /* POST /orders/$ID/refund: */
   1297     {
   1298       .url_prefix = "/orders/",
   1299       .have_id_segment = true,
   1300       .allow_deleted_instance = true,
   1301       .url_suffix = "refund",
   1302       .method = MHD_HTTP_METHOD_POST,
   1303       .handler = &TMH_post_orders_ID_refund,
   1304       /* the body should be pretty small, allow 1 MB of upload
   1305          to set a conservative bound for sane wallets */
   1306       .max_upload = 1024 * 1024
   1307     },
   1308     /* GET /orders/$ID: */
   1309     {
   1310       .url_prefix = "/orders/",
   1311       .method = MHD_HTTP_METHOD_GET,
   1312       .allow_deleted_instance = true,
   1313       .have_id_segment = true,
   1314       .handler = &TMH_get_orders_ID
   1315     },
   1316     /* GET /sessions/$ID: */
   1317     {
   1318       .url_prefix = "/sessions/",
   1319       .method = MHD_HTTP_METHOD_GET,
   1320       .allow_deleted_instance = true,
   1321       .have_id_segment = true,
   1322       .handler = &TMH_get_sessions_ID
   1323     },
   1324     /* GET /static/ *: */
   1325     {
   1326       .url_prefix = "/static/",
   1327       .method = MHD_HTTP_METHOD_GET,
   1328       .have_id_segment = true,
   1329       .handler = &TMH_return_static
   1330     },
   1331     /* POST /reports/$ID/ */
   1332     {
   1333       .url_prefix = "/reports/",
   1334       .method = MHD_HTTP_METHOD_POST,
   1335       .have_id_segment = true,
   1336       .handler = &TMH_post_reports_ID,
   1337     },
   1338     /* GET /templates/$ID/: */
   1339     {
   1340       .url_prefix = "/templates/",
   1341       .method = MHD_HTTP_METHOD_GET,
   1342       .have_id_segment = true,
   1343       .handler = &TMH_get_templates_ID
   1344     },
   1345     /* GET /products/$HASH/image: */
   1346     {
   1347       .url_prefix = "/products/",
   1348       .method = MHD_HTTP_METHOD_GET,
   1349       .have_id_segment = true,
   1350       .allow_deleted_instance = true,
   1351       .url_suffix = "image",
   1352       .handler = &TMH_get_products_image
   1353     },
   1354     /* POST /templates/$ID: */
   1355     {
   1356       .url_prefix = "/templates/",
   1357       .method = MHD_HTTP_METHOD_POST,
   1358       .have_id_segment = true,
   1359       .handler = &TMH_post_using_templates_ID,
   1360       .max_upload = 1024 * 1024
   1361     },
   1362     /* POST /challenge/$ID: */
   1363     {
   1364       .url_prefix = "/challenge/",
   1365       .method = MHD_HTTP_METHOD_POST,
   1366       .have_id_segment = true,
   1367       .handler = &TMH_post_challenge_ID,
   1368       .max_upload = 1024
   1369     },
   1370     /* POST /challenge/$ID/confirm: */
   1371     {
   1372       .url_prefix = "/challenge/",
   1373       .method = MHD_HTTP_METHOD_POST,
   1374       .have_id_segment = true,
   1375       .url_suffix = "confirm",
   1376       .handler = &TMH_post_challenge_ID_confirm,
   1377       .max_upload = 1024
   1378     },
   1379     /* POST /instances */
   1380     {
   1381       .url_prefix = "/instances",
   1382       .method = MHD_HTTP_METHOD_POST,
   1383       .skip_instance = true,
   1384       .default_only = true,
   1385       .handler = &TMH_public_post_instances,
   1386       /* allow instance data of up to 8 MB, that should be plenty;
   1387          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1388          would require further changes to the allocation logic
   1389          in the code... */
   1390       .max_upload = 1024 * 1024 * 8
   1391     },
   1392     /* POST /forgot-password: */
   1393     {
   1394       .url_prefix = "/forgot-password",
   1395       .method = MHD_HTTP_METHOD_POST,
   1396       .handler = &TMH_public_post_instances_ID_auth,
   1397       /* Body should be pretty small. */
   1398       .max_upload = 1024 * 1024
   1399     },
   1400 
   1401     {
   1402       .url_prefix = "*",
   1403       .method = MHD_HTTP_METHOD_OPTIONS,
   1404       .handler = &handle_server_options
   1405     },
   1406     {
   1407       .url_prefix = NULL
   1408     }
   1409   };
   1410   const char *management_prefix = "/management/";
   1411   const char *private_prefix = "/private/";
   1412   const char *url = *urlp;
   1413   struct TMH_RequestHandler *handlers;
   1414 
   1415   *is_public = false; /* ensure safe default */
   1416   if ( (0 == strncmp (url,
   1417                       management_prefix,
   1418                       strlen (management_prefix))) )
   1419   {
   1420     handlers = management_handlers;
   1421     *urlp = url + strlen (management_prefix) - 1;
   1422   }
   1423   else if ( (0 == strncmp (url,
   1424                            private_prefix,
   1425                            strlen (private_prefix))) ||
   1426             (0 == strcmp (url,
   1427                           "/private")) )
   1428   {
   1429     handlers = private_handlers;
   1430     if (0 == strcmp (url,
   1431                      "/private"))
   1432       *urlp = "/";
   1433     else
   1434       *urlp = url + strlen (private_prefix) - 1;
   1435   }
   1436   else
   1437   {
   1438     handlers = public_handlers;
   1439     *is_public = true;
   1440   }
   1441   return handlers;
   1442 }
   1443 
   1444 
   1445 /**
   1446  * Checks if the @a rh matches the given (parsed) URL.
   1447  *
   1448  * @param rh handler to compare against
   1449  * @param url the main URL (without "/private/" prefix, if any)
   1450  * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config'
   1451  * @param infix_url infix text, i.e. "$ORDER_ID".
   1452  * @param infix_strlen length of the string in @a infix_url
   1453  * @param suffix_url suffix, i.e. "/refund", including the "/"
   1454  * @param suffix_strlen number of characters in @a suffix_url
   1455  * @return true if @a rh matches this request
   1456  */
   1457 static bool
   1458 prefix_match (const struct TMH_RequestHandler *rh,
   1459               const char *url,
   1460               size_t prefix_strlen,
   1461               const char *infix_url,
   1462               size_t infix_strlen,
   1463               const char *suffix_url,
   1464               size_t suffix_strlen)
   1465 {
   1466   if ( (prefix_strlen != strlen (rh->url_prefix)) ||
   1467        (0 != memcmp (url,
   1468                      rh->url_prefix,
   1469                      prefix_strlen)) )
   1470     return false;
   1471   if (! rh->have_id_segment)
   1472   {
   1473     /* Require /$PREFIX/$SUFFIX or /$PREFIX */
   1474     if (NULL != suffix_url)
   1475       return false;       /* too many segments to match */
   1476     if ( (NULL == infix_url)   /* either or */
   1477          ^ (NULL == rh->url_suffix) )
   1478       return false;       /* suffix existence mismatch */
   1479     /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */
   1480     if ( (NULL != infix_url) &&
   1481          ( (infix_strlen != strlen (rh->url_suffix)) ||
   1482            (0 != memcmp (infix_url,
   1483                          rh->url_suffix,
   1484                          infix_strlen)) ) )
   1485       return false;       /* cannot use infix as suffix: content mismatch */
   1486   }
   1487   else
   1488   {
   1489     /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */
   1490     if (NULL == infix_url)
   1491       return false;       /* infix existence mismatch */
   1492     if ( ( (NULL == suffix_url)
   1493            ^ (NULL == rh->url_suffix) ) )
   1494       return false;       /* suffix existence mismatch */
   1495     if ( (NULL != suffix_url) &&
   1496          ( (suffix_strlen != strlen (rh->url_suffix)) ||
   1497            (0 != memcmp (suffix_url,
   1498                          rh->url_suffix,
   1499                          suffix_strlen)) ) )
   1500       return false;       /* suffix content mismatch */
   1501   }
   1502   return true;
   1503 }
   1504 
   1505 
   1506 /**
   1507  * Identify the handler of the request from the @a url and @a method
   1508  *
   1509  * @param[in,out] hc handler context to update with applicable handler
   1510  * @param handlers array of handlers to consider
   1511  * @param url URL to match against the handlers
   1512  * @param method HTTP access method to consider
   1513  * @param use_admin set to true if we are using the admin instance
   1514  * @return #GNUNET_OK on success,
   1515  *         #GNUNET_NO if an error was queued (return #MHD_YES)
   1516  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
   1517  */
   1518 static enum GNUNET_GenericReturnValue
   1519 identify_handler (struct TMH_HandlerContext *hc,
   1520                   const struct TMH_RequestHandler *handlers,
   1521                   const char *url,
   1522                   const char *method,
   1523                   bool use_admin)
   1524 {
   1525   size_t prefix_strlen;   /* i.e. 8 for "/orders/", or 7 for "/config" */
   1526   const char *infix_url = NULL;   /* i.e. "$ORDER_ID", no '/'-es */
   1527   size_t infix_strlen = 0;   /* number of characters in infix_url */
   1528   const char *suffix_url = NULL;   /* i.e. "refund", excludes '/' at the beginning */
   1529   size_t suffix_strlen = 0;   /* number of characters in suffix_url */
   1530 
   1531   if (0 == strcasecmp (method,
   1532                        MHD_HTTP_METHOD_HEAD))
   1533     method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
   1534   if (0 == strcmp (url,
   1535                    ""))
   1536     url = "/"; /* code below does not like empty string */
   1537 
   1538   /* parse the URL into the three different components */
   1539   {
   1540     const char *slash;
   1541 
   1542     slash = strchr (&url[1], '/');
   1543     if (NULL == slash)
   1544     {
   1545       /* the prefix was everything */
   1546       prefix_strlen = strlen (url);
   1547     }
   1548     else
   1549     {
   1550       prefix_strlen = slash - url + 1;   /* includes both '/'-es if present! */
   1551       infix_url = slash + 1;
   1552       slash = strchr (infix_url, '/');
   1553       if (NULL == slash)
   1554       {
   1555         /* the infix was the rest */
   1556         infix_strlen = strlen (infix_url);
   1557       }
   1558       else
   1559       {
   1560         infix_strlen = slash - infix_url;   /* excludes both '/'-es */
   1561         suffix_url = slash + 1;   /* skip the '/' */
   1562         suffix_strlen = strlen (suffix_url);
   1563       }
   1564       /* free any previously set infix in case the request is being
   1565          re-dispatched (e.g. via POST /reports/$REPORT_ID), otherwise
   1566          the earlier infix would be leaked */
   1567       GNUNET_free (hc->infix);
   1568       hc->infix = GNUNET_strndup (infix_url,
   1569                                   infix_strlen);
   1570     }
   1571   }
   1572 
   1573   /* find matching handler */
   1574   {
   1575     bool url_found = false;
   1576 
   1577     for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
   1578     {
   1579       const struct TMH_RequestHandler *rh = &handlers[i];
   1580 
   1581       if (rh->default_only && (! use_admin))
   1582         continue;
   1583       if (! prefix_match (rh,
   1584                           url,
   1585                           prefix_strlen,
   1586                           infix_url,
   1587                           infix_strlen,
   1588                           suffix_url,
   1589                           suffix_strlen))
   1590         continue;
   1591       url_found = true;
   1592       if (0 == strcasecmp (method,
   1593                            MHD_HTTP_METHOD_OPTIONS))
   1594       {
   1595         return (MHD_YES ==
   1596                 TALER_MHD_reply_cors_preflight (hc->connection))
   1597             ? GNUNET_NO
   1598             : GNUNET_SYSERR;
   1599       }
   1600       if ( (rh->method != NULL) &&
   1601            (0 != strcasecmp (method,
   1602                              rh->method)) )
   1603         continue;
   1604       hc->rh = rh;
   1605       break;
   1606     }
   1607     /* Handle HTTP 405: METHOD NOT ALLOWED case */
   1608     if ( (NULL == hc->rh) &&
   1609          (url_found) )
   1610     {
   1611       struct MHD_Response *reply;
   1612       enum MHD_Result ret;
   1613       char *allowed = NULL;
   1614 
   1615       GNUNET_break_op (0);
   1616       /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */
   1617       for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
   1618       {
   1619         const struct TMH_RequestHandler *rh = &handlers[i];
   1620 
   1621         if (rh->default_only && (! use_admin))
   1622           continue;
   1623         if (! prefix_match (rh,
   1624                             url,
   1625                             prefix_strlen,
   1626                             infix_url,
   1627                             infix_strlen,
   1628                             suffix_url,
   1629                             suffix_strlen))
   1630           continue;
   1631         if (NULL == allowed)
   1632         {
   1633           allowed = GNUNET_strdup (rh->method);
   1634         }
   1635         else
   1636         {
   1637           char *tmp;
   1638 
   1639           GNUNET_asprintf (&tmp,
   1640                            "%s, %s",
   1641                            allowed,
   1642                            rh->method);
   1643           GNUNET_free (allowed);
   1644           allowed = tmp;
   1645         }
   1646         if (0 == strcasecmp (rh->method,
   1647                              MHD_HTTP_METHOD_GET))
   1648         {
   1649           char *tmp;
   1650 
   1651           GNUNET_asprintf (&tmp,
   1652                            "%s, %s",
   1653                            allowed,
   1654                            MHD_HTTP_METHOD_HEAD);
   1655           GNUNET_free (allowed);
   1656           allowed = tmp;
   1657         }
   1658       }
   1659       reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
   1660                                     method);
   1661       GNUNET_break (MHD_YES ==
   1662                     MHD_add_response_header (reply,
   1663                                              MHD_HTTP_HEADER_ALLOW,
   1664                                              allowed));
   1665       GNUNET_free (allowed);
   1666       ret = MHD_queue_response (hc->connection,
   1667                                 MHD_HTTP_METHOD_NOT_ALLOWED,
   1668                                 reply);
   1669       MHD_destroy_response (reply);
   1670       return (MHD_YES == ret)
   1671           ? GNUNET_NO
   1672           : GNUNET_SYSERR;
   1673     }
   1674     if (NULL == hc->rh)
   1675     {
   1676       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1677                   "Endpoint `%s' not known\n",
   1678                   hc->url);
   1679       return (MHD_YES ==
   1680               TALER_MHD_reply_with_error (hc->connection,
   1681                                           MHD_HTTP_NOT_FOUND,
   1682                                           TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
   1683                                           hc->url))
   1684           ? GNUNET_NO
   1685           : GNUNET_SYSERR;
   1686     }
   1687   }
   1688   return GNUNET_OK;
   1689 }
   1690 
   1691 
   1692 enum GNUNET_GenericReturnValue
   1693 TMH_dispatch_request (struct TMH_HandlerContext *hc,
   1694                       const char *url,
   1695                       const char *method,
   1696                       bool use_admin,
   1697                       bool *is_public)
   1698 {
   1699   const struct TMH_RequestHandler *handlers;
   1700 
   1701   *is_public = false;
   1702   handlers = determine_handler_group (&url,
   1703                                       is_public);
   1704   return identify_handler (hc,
   1705                            handlers,
   1706                            url,
   1707                            method,
   1708                            use_admin);
   1709 }