merchant

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

taler-merchant-httpd_dispatcher.c (54263B)


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