merchant

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

taler-merchant-httpd_dispatcher.c (53027B)


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