merchant

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

taler-merchant-httpd_dispatcher.c (52855B)


      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       .handler = &TMH_private_get_units
    417     },
    418     /* POST /units: */
    419     {
    420       .url_prefix = "/units",
    421       .method = MHD_HTTP_METHOD_POST,
    422       .permission = "units-write",
    423       .handler = &TMH_private_post_units,
    424       .max_upload = 1024 * 8
    425     },
    426     /* GET /units/$UNIT: */
    427     {
    428       .url_prefix = "/units/",
    429       .method = MHD_HTTP_METHOD_GET,
    430       .have_id_segment = true,
    431       .allow_deleted_instance = true,
    432       .handler = &TMH_private_get_units_ID
    433     },
    434     /* DELETE /units/$UNIT: */
    435     {
    436       .url_prefix = "/units/",
    437       .method = MHD_HTTP_METHOD_DELETE,
    438       .permission = "units-write",
    439       .have_id_segment = true,
    440       .allow_deleted_instance = true,
    441       .handler = &TMH_private_delete_units_ID
    442     },
    443     /* PATCH /units/$UNIT: */
    444     {
    445       .url_prefix = "/units/",
    446       .method = MHD_HTTP_METHOD_PATCH,
    447       .permission = "units-write",
    448       .have_id_segment = true,
    449       .allow_deleted_instance = true,
    450       .handler = &TMH_private_patch_units_ID,
    451       .max_upload = 1024 * 8
    452     },
    453     /* GET /products: */
    454     {
    455       .url_prefix = "/products",
    456       .permission = "products-read",
    457       .method = MHD_HTTP_METHOD_GET,
    458       .handler = &TMH_private_get_products
    459     },
    460     /* POST /products: */
    461     {
    462       .url_prefix = "/products",
    463       .method = MHD_HTTP_METHOD_POST,
    464       .permission = "products-write",
    465       .handler = &TMH_private_post_products,
    466       /* allow product data of up to 8 MB, that should be plenty;
    467          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    468          would require further changes to the allocation logic
    469          in the code... */
    470       .max_upload = 1024 * 1024 * 8
    471     },
    472     /* GET /products/$ID: */
    473     {
    474       .url_prefix = "/products/",
    475       .method = MHD_HTTP_METHOD_GET,
    476       .have_id_segment = true,
    477       .permission = "products-read",
    478       .allow_deleted_instance = true,
    479       .handler = &TMH_private_get_products_ID
    480     },
    481     /* DELETE /products/$ID/: */
    482     {
    483       .url_prefix = "/products/",
    484       .method = MHD_HTTP_METHOD_DELETE,
    485       .have_id_segment = true,
    486       .permission = "products-write",
    487       .allow_deleted_instance = true,
    488       .handler = &TMH_private_delete_products_ID
    489     },
    490     /* PATCH /products/$ID/: */
    491     {
    492       .url_prefix = "/products/",
    493       .method = MHD_HTTP_METHOD_PATCH,
    494       .have_id_segment = true,
    495       .allow_deleted_instance = true,
    496       .permission = "products-write",
    497       .handler = &TMH_private_patch_products_ID,
    498       /* allow product data of up to 8 MB, that should be plenty;
    499          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    500          would require further changes to the allocation logic
    501          in the code... */
    502       .max_upload = 1024 * 1024 * 8
    503     },
    504     /* POST /products/$ID/lock: */
    505     {
    506       .url_prefix = "/products/",
    507       .url_suffix = "lock",
    508       .method = MHD_HTTP_METHOD_POST,
    509       .have_id_segment = true,
    510       .permission = "products-lock",
    511       .handler = &TMH_private_post_products_ID_lock,
    512       /* the body should be pretty small, allow 1 MB of upload
    513          to set a conservative bound for sane wallets */
    514       .max_upload = 1024 * 1024
    515     },
    516     /* POST /orders: */
    517     {
    518       .url_prefix = "/orders",
    519       .method = MHD_HTTP_METHOD_POST,
    520       .permission = "orders-write",
    521       .handler = &TMH_private_post_orders,
    522       /* allow contracts of up to 8 MB, that should be plenty;
    523          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    524          would require further changes to the allocation logic
    525          in the code... */
    526       .max_upload = 1024 * 1024 * 8
    527     },
    528     /* GET /orders/$ID: */
    529     {
    530       .url_prefix = "/orders/",
    531       .method = MHD_HTTP_METHOD_GET,
    532       .permission = "orders-read",
    533       .have_id_segment = true,
    534       .allow_deleted_instance = true,
    535       .handler = &TMH_private_get_orders_ID
    536     },
    537     /* GET /orders: */
    538     {
    539       .url_prefix = "/orders",
    540       .method = MHD_HTTP_METHOD_GET,
    541       .permission = "orders-read",
    542       .allow_deleted_instance = true,
    543       .handler = &TMH_private_get_orders
    544     },
    545     /* POST /orders/$ID/refund: */
    546     {
    547       .url_prefix = "/orders/",
    548       .url_suffix = "refund",
    549       .method = MHD_HTTP_METHOD_POST,
    550       .have_id_segment = true,
    551       .permission = "orders-refund",
    552       .handler = &TMH_private_post_orders_ID_refund,
    553       /* the body should be pretty small, allow 1 MB of upload
    554          to set a conservative bound for sane wallets */
    555       .max_upload = 1024 * 1024
    556     },
    557     /* PATCH /orders/$ID/forget: */
    558     {
    559       .url_prefix = "/orders/",
    560       .url_suffix = "forget",
    561       .method = MHD_HTTP_METHOD_PATCH,
    562       .permission = "orders-write",
    563       .have_id_segment = true,
    564       .allow_deleted_instance = true,
    565       .handler = &TMH_private_patch_orders_ID_forget,
    566       /* the body should be pretty small, allow 1 MB of upload
    567          to set a conservative bound for sane wallets */
    568       .max_upload = 1024 * 1024
    569     },
    570     /* DELETE /orders/$ID: */
    571     {
    572       .url_prefix = "/orders/",
    573       .method = MHD_HTTP_METHOD_DELETE,
    574       .permission = "orders-write",
    575       .have_id_segment = true,
    576       .allow_deleted_instance = true,
    577       .handler = &TMH_private_delete_orders_ID
    578     },
    579     /* POST /transfers: */
    580     {
    581       .url_prefix = "/transfers",
    582       .method = MHD_HTTP_METHOD_POST,
    583       .allow_deleted_instance = true,
    584       .handler = &TMH_private_post_transfers,
    585       .permission = "transfers-write",
    586       /* the body should be pretty small, allow 1 MB of upload
    587          to set a conservative bound for sane wallets */
    588       .max_upload = 1024 * 1024
    589     },
    590     /* DELETE /transfers/$ID: */
    591     {
    592       .url_prefix = "/transfers/",
    593       .method = MHD_HTTP_METHOD_DELETE,
    594       .permission = "transfers-write",
    595       .allow_deleted_instance = true,
    596       .handler = &TMH_private_delete_transfers_ID,
    597       .have_id_segment = true,
    598       /* the body should be pretty small, allow 1 MB of upload
    599          to set a conservative bound for sane wallets */
    600       .max_upload = 1024 * 1024
    601     },
    602     /* GET /transfers: */
    603     {
    604       .url_prefix = "/transfers",
    605       .permission = "transfers-read",
    606       .method = MHD_HTTP_METHOD_GET,
    607       .allow_deleted_instance = true,
    608       .handler = &TMH_private_get_transfers
    609     },
    610     /* GET /incoming: */
    611     {
    612       .url_prefix = "/incoming",
    613       .permission = "transfers-read",
    614       .method = MHD_HTTP_METHOD_GET,
    615       .allow_deleted_instance = true,
    616       .handler = &TMH_private_get_incoming
    617     },
    618     /* GET /incoming/$ID: */
    619     {
    620       .url_prefix = "/incoming/",
    621       .permission = "transfers-read",
    622       .method = MHD_HTTP_METHOD_GET,
    623       .allow_deleted_instance = true,
    624       .have_id_segment = true,
    625       .handler = &TMH_private_get_incoming_ID
    626     },
    627     /* POST /otp-devices: */
    628     {
    629       .url_prefix = "/otp-devices",
    630       .permission = "otp-devices-write",
    631       .method = MHD_HTTP_METHOD_POST,
    632       .handler = &TMH_private_post_otp_devices
    633     },
    634     /* GET /otp-devices: */
    635     {
    636       .url_prefix = "/otp-devices",
    637       .permission = "opt-devices-read",
    638       .method = MHD_HTTP_METHOD_GET,
    639       .handler = &TMH_private_get_otp_devices
    640     },
    641     /* GET /otp-devices/$ID: */
    642     {
    643       .url_prefix = "/otp-devices/",
    644       .method = MHD_HTTP_METHOD_GET,
    645       .permission = "otp-devices-read",
    646       .have_id_segment = true,
    647       .handler = &TMH_private_get_otp_devices_ID
    648     },
    649     /* DELETE /otp-devices/$ID: */
    650     {
    651       .url_prefix = "/otp-devices/",
    652       .method = MHD_HTTP_METHOD_DELETE,
    653       .permission = "otp-devices-write",
    654       .have_id_segment = true,
    655       .handler = &TMH_private_delete_otp_devices_ID
    656     },
    657     /* PATCH /otp-devices/$ID: */
    658     {
    659       .url_prefix = "/otp-devices/",
    660       .method = MHD_HTTP_METHOD_PATCH,
    661       .permission = "otp-devices-write",
    662       .have_id_segment = true,
    663       .handler = &TMH_private_patch_otp_devices_ID
    664     },
    665     /* POST /templates: */
    666     {
    667       .url_prefix = "/templates",
    668       .method = MHD_HTTP_METHOD_POST,
    669       .permission = "templates-write",
    670       .handler = &TMH_private_post_templates,
    671       /* allow template data of up to 8 MB, that should be plenty;
    672          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    673          would require further changes to the allocation logic
    674          in the code... */
    675       .max_upload = 1024 * 1024 * 8
    676     },
    677     /* GET /templates: */
    678     {
    679       .url_prefix = "/templates",
    680       .permission = "templates-read",
    681       .method = MHD_HTTP_METHOD_GET,
    682       .handler = &TMH_private_get_templates
    683     },
    684     /* GET /templates/$ID/: */
    685     {
    686       .url_prefix = "/templates/",
    687       .method = MHD_HTTP_METHOD_GET,
    688       .permission = "templates-read",
    689       .have_id_segment = true,
    690       .allow_deleted_instance = true,
    691       .handler = &TMH_private_get_templates_ID
    692     },
    693     /* DELETE /templates/$ID/: */
    694     {
    695       .url_prefix = "/templates/",
    696       .method = MHD_HTTP_METHOD_DELETE,
    697       .permission = "templates-write",
    698       .have_id_segment = true,
    699       .allow_deleted_instance = true,
    700       .handler = &TMH_private_delete_templates_ID
    701     },
    702     /* PATCH /templates/$ID/: */
    703     {
    704       .url_prefix = "/templates/",
    705       .method = MHD_HTTP_METHOD_PATCH,
    706       .permission = "templates-write",
    707       .have_id_segment = true,
    708       .allow_deleted_instance = true,
    709       .handler = &TMH_private_patch_templates_ID,
    710       /* allow template data of up to 8 MB, that should be plenty;
    711          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    712          would require further changes to the allocation logic
    713          in the code... */
    714       .max_upload = 1024 * 1024 * 8
    715     },
    716 
    717     /* POST /pots: */
    718     {
    719       .url_prefix = "/pots",
    720       .method = MHD_HTTP_METHOD_POST,
    721       .permission = "pots-write",
    722       .handler = &TMH_private_post_pots,
    723     },
    724     /* GET /pots: */
    725     {
    726       .url_prefix = "/pots",
    727       .permission = "pots-read",
    728       .method = MHD_HTTP_METHOD_GET,
    729       .handler = &TMH_private_get_pots
    730     },
    731     /* DELETE /pots/$ID: */
    732     {
    733       .url_prefix = "/pots/",
    734       .method = MHD_HTTP_METHOD_DELETE,
    735       .permission = "pots-write",
    736       .have_id_segment = true,
    737       .handler = &TMH_private_delete_pot
    738     },
    739     /* PATCH /pots/$ID: */
    740     {
    741       .url_prefix = "/pots/",
    742       .method = MHD_HTTP_METHOD_PATCH,
    743       .permission = "pots-write",
    744       .have_id_segment = true,
    745       .handler = &TMH_private_patch_pot,
    746     },
    747 
    748     /* GET /webhooks: */
    749     {
    750       .url_prefix = "/webhooks",
    751       .permission = "webhooks-read",
    752       .method = MHD_HTTP_METHOD_GET,
    753       .handler = &TMH_private_get_webhooks
    754     },
    755     /* POST /webhooks: */
    756     {
    757       .url_prefix = "/webhooks",
    758       .method = MHD_HTTP_METHOD_POST,
    759       .permission = "webhooks-write",
    760       .handler = &TMH_private_post_webhooks,
    761       /* allow webhook data of up to 8 MB, that should be plenty;
    762          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    763          would require further changes to the allocation logic
    764          in the code... */
    765       .max_upload = 1024 * 1024 * 8
    766     },
    767     /* GET /webhooks/$ID/: */
    768     {
    769       .url_prefix = "/webhooks/",
    770       .method = MHD_HTTP_METHOD_GET,
    771       .permission = "webhooks-read",
    772       .have_id_segment = true,
    773       .allow_deleted_instance = true,
    774       .handler = &TMH_private_get_webhooks_ID
    775     },
    776     /* DELETE /webhooks/$ID/: */
    777     {
    778       .url_prefix = "/webhooks/",
    779       .permission = "webhooks-write",
    780       .method = MHD_HTTP_METHOD_DELETE,
    781       .have_id_segment = true,
    782       .allow_deleted_instance = true,
    783       .handler = &TMH_private_delete_webhooks_ID
    784     },
    785     /* PATCH /webhooks/$ID/: */
    786     {
    787       .url_prefix = "/webhooks/",
    788       .method = MHD_HTTP_METHOD_PATCH,
    789       .permission = "webhooks-write",
    790       .have_id_segment = true,
    791       .allow_deleted_instance = true,
    792       .handler = &TMH_private_patch_webhooks_ID,
    793       /* allow webhook data of up to 8 MB, that should be plenty;
    794          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
    795          would require further changes to the allocation logic
    796          in the code... */
    797       .max_upload = 1024 * 1024 * 8
    798     },
    799     /* POST /accounts: */
    800     {
    801       .url_prefix = "/accounts",
    802       .method = MHD_HTTP_METHOD_POST,
    803       .permission = "accounts-write",
    804       .handler = &TMH_private_post_account,
    805       /* allow account details of up to 8 kb, that should be plenty */
    806       .max_upload = 1024 * 8
    807     },
    808     /* POST /accounts/H_WIRE/kycauth: */
    809     {
    810       .url_prefix = "/accounts/",
    811       .url_suffix = "kycauth",
    812       .method = MHD_HTTP_METHOD_POST,
    813       .have_id_segment = true,
    814       .permission = "accounts-read",
    815       .handler = &TMH_private_post_accounts_H_WIRE_kycauth,
    816       /* allow exchange URL up to 4 kb, that should be plenty */
    817       .max_upload = 1024 * 4
    818     },
    819     /* POST /accept-tos-early: */
    820     {
    821       .url_prefix = "/accept-tos-early",
    822       .method = MHD_HTTP_METHOD_POST,
    823       .permission = "accounts-write",
    824       .handler = &TMH_private_post_accept_tos_early,
    825       /* allow exchange URL plus terms version up to 4 kb */
    826       .max_upload = 1024 * 4
    827     },
    828     /* PATCH /accounts/$H_WIRE: */
    829     {
    830       .url_prefix = "/accounts/",
    831       .method = MHD_HTTP_METHOD_PATCH,
    832       .permission = "accounts-write",
    833       .handler = &TMH_private_patch_accounts_ID,
    834       .have_id_segment = true,
    835       /* allow account details of up to 8 kb, that should be plenty */
    836       .max_upload = 1024 * 8
    837     },
    838     /* GET /accounts: */
    839     {
    840       .url_prefix = "/accounts",
    841       .permission = "accounts-read",
    842       .method = MHD_HTTP_METHOD_GET,
    843       .handler = &TMH_private_get_accounts
    844     },
    845     /* GET /accounts/$H_WIRE: */
    846     {
    847       .url_prefix = "/accounts/",
    848       .permission = "accounts-read",
    849       .method = MHD_HTTP_METHOD_GET,
    850       .have_id_segment = true,
    851       .handler = &TMH_private_get_accounts_ID
    852     },
    853     /* DELETE /accounts/$H_WIRE: */
    854     {
    855       .url_prefix = "/accounts/",
    856       .permission = "accounts-write",
    857       .method = MHD_HTTP_METHOD_DELETE,
    858       .handler = &TMH_private_delete_account_ID,
    859       .have_id_segment = true
    860     },
    861     /* GET /tokens: */
    862     {
    863       .url_prefix = "/tokens",
    864       .permission = "tokens-read",
    865       .method = MHD_HTTP_METHOD_GET,
    866       .handler = &TMH_private_get_instances_ID_tokens,
    867     },
    868     /* POST /token: */
    869     {
    870       .url_prefix = "/token",
    871       .permission = "token-refresh",
    872       .method = MHD_HTTP_METHOD_POST,
    873       .handler = &TMH_private_post_instances_ID_token,
    874       /* Body should be tiny. */
    875       .max_upload = 1024
    876     },
    877     /* DELETE /tokens/$SERIAL: */
    878     {
    879       .url_prefix = "/tokens/",
    880       .permission = "tokens-write",
    881       .method = MHD_HTTP_METHOD_DELETE,
    882       .handler = &TMH_private_delete_instances_ID_token_SERIAL,
    883       .have_id_segment = true
    884     },
    885     /* DELETE /token: */
    886     {
    887       .url_prefix = "/token",
    888       .method = MHD_HTTP_METHOD_DELETE,
    889       .handler = &TMH_private_delete_instances_ID_token,
    890       .permission = NULL /* No ACL: anyone can delete any token they have */
    891     },
    892     /* GET /tokenfamilies: */
    893     {
    894       .url_prefix = "/tokenfamilies",
    895       .permission = "tokenfamilies-read",
    896       .method = MHD_HTTP_METHOD_GET,
    897       .handler = &TMH_private_get_tokenfamilies
    898     },
    899     /* POST /tokenfamilies: */
    900     {
    901       .url_prefix = "/tokenfamilies",
    902       .permission = "tokenfamilies-write",
    903       .method = MHD_HTTP_METHOD_POST,
    904       .handler = &TMH_private_post_token_families
    905     },
    906     /* GET /tokenfamilies/$SLUG/: */
    907     {
    908       .url_prefix = "/tokenfamilies/",
    909       .method = MHD_HTTP_METHOD_GET,
    910       .permission = "tokenfamilies-read",
    911       .have_id_segment = true,
    912       .handler = &TMH_private_get_tokenfamilies_SLUG
    913     },
    914     /* DELETE /tokenfamilies/$SLUG/: */
    915     {
    916       .url_prefix = "/tokenfamilies/",
    917       .method = MHD_HTTP_METHOD_DELETE,
    918       .permission = "tokenfamilies-write",
    919       .have_id_segment = true,
    920       .handler = &TMH_private_delete_token_families_SLUG
    921     },
    922     /* PATCH /tokenfamilies/$SLUG/: */
    923     {
    924       .url_prefix = "/tokenfamilies/",
    925       .method = MHD_HTTP_METHOD_PATCH,
    926       .permission = "tokenfamilies-write",
    927       .have_id_segment = true,
    928       .handler = &TMH_private_patch_token_family_SLUG,
    929     },
    930 
    931     /* Reports endpoints */
    932     {
    933       .url_prefix = "/reports",
    934       .method = MHD_HTTP_METHOD_GET,
    935       .permission = "reports-read",
    936       .handler = &TMH_private_get_reports,
    937     },
    938     {
    939       .url_prefix = "/reports",
    940       .method = MHD_HTTP_METHOD_POST,
    941       .permission = "reports-write",
    942       .handler = &TMH_private_post_reports,
    943     },
    944     {
    945       .url_prefix = "/reports/",
    946       .method = MHD_HTTP_METHOD_GET,
    947       .handler = &TMH_private_get_report,
    948       .permission = "reports-read",
    949       .have_id_segment = true,
    950     },
    951     {
    952       .url_prefix = "/reports/",
    953       .method = MHD_HTTP_METHOD_PATCH,
    954       .handler = &TMH_private_patch_report,
    955       .permission = "reports-write",
    956       .have_id_segment = true,
    957     },
    958     {
    959       .url_prefix = "/reports/",
    960       .method = MHD_HTTP_METHOD_DELETE,
    961       .handler = &TMH_private_delete_report,
    962       .permission = "reports-write",
    963       .have_id_segment = true,
    964     },
    965 
    966     /* Groups endpoints */
    967     {
    968       .url_prefix = "/groups",
    969       .method = MHD_HTTP_METHOD_GET,
    970       .permission = "groups-read",
    971       .handler = &TMH_private_get_groups,
    972     },
    973     {
    974       .url_prefix = "/groups",
    975       .method = MHD_HTTP_METHOD_POST,
    976       .permission = "groups-write",
    977       .handler = &TMH_private_post_groups,
    978     },
    979     {
    980       .url_prefix = "/groups/",
    981       .method = MHD_HTTP_METHOD_PATCH,
    982       .handler = &TMH_private_patch_group,
    983       .permission = "groups-write",
    984       .have_id_segment = true,
    985     },
    986     {
    987       .url_prefix = "/groups/",
    988       .method = MHD_HTTP_METHOD_DELETE,
    989       .handler = &TMH_private_delete_group,
    990       .permission = "groups-write",
    991       .have_id_segment = true,
    992     },
    993 
    994     /* Money pots endpoints */
    995     {
    996       .url_prefix = "/pots",
    997       .method = MHD_HTTP_METHOD_GET,
    998       .handler = &TMH_private_get_pots,
    999       .permission = "pots-read",
   1000     },
   1001     {
   1002       .url_prefix = "/pots",
   1003       .method = MHD_HTTP_METHOD_POST,
   1004       .handler = &TMH_private_post_pots,
   1005       .permission = "pots-write"
   1006     },
   1007     {
   1008       .url_prefix = "/pots/",
   1009       .method = MHD_HTTP_METHOD_GET,
   1010       .handler = &TMH_private_get_pot,
   1011       .have_id_segment = true,
   1012       .permission =  "pots-read",
   1013     },
   1014     {
   1015       .url_prefix = "/pots/",
   1016       .method = MHD_HTTP_METHOD_PATCH,
   1017       .handler = &TMH_private_patch_pot,
   1018       .have_id_segment = true,
   1019       .permission = "pots-write"
   1020     },
   1021     {
   1022       .url_prefix = "/pots/",
   1023       .method = MHD_HTTP_METHOD_DELETE,
   1024       .handler = &TMH_private_delete_pot,
   1025       .have_id_segment = true,
   1026       .permission = "pots-write"
   1027     },
   1028 
   1029     /* GET /donau */
   1030     {
   1031       .url_prefix = "/donau",
   1032       .method = MHD_HTTP_METHOD_GET,
   1033       .handler = &TMH_private_get_donau_instances
   1034     },
   1035     /* POST /donau */
   1036     {
   1037       .url_prefix = "/donau",
   1038       .method = MHD_HTTP_METHOD_POST,
   1039       .handler = &TMH_private_post_donau_instance
   1040     },
   1041     /* DELETE /donau/$charity-id */
   1042     {
   1043       .url_prefix = "/donau/",
   1044       .method = MHD_HTTP_METHOD_DELETE,
   1045       .have_id_segment = true,
   1046       .handler = &TMH_private_delete_donau_instance_ID
   1047     },
   1048     /* GET /statistics-counter/$SLUG: */
   1049     {
   1050       .url_prefix = "/statistics-counter/",
   1051       .method = MHD_HTTP_METHOD_GET,
   1052       .permission = "statistics-read",
   1053       .have_id_segment = true,
   1054       .handler = &TMH_private_get_statistics_counter_SLUG,
   1055     },
   1056     /* GET /statistics-amount/$SLUG: */
   1057     {
   1058       .url_prefix = "/statistics-amount/",
   1059       .method = MHD_HTTP_METHOD_GET,
   1060       .permission = "statistics-read",
   1061       .have_id_segment = true,
   1062       .handler = &TMH_private_get_statistics_amount_SLUG,
   1063     },
   1064     /* GET /statistics-report/transactions: */
   1065     {
   1066       .url_prefix = "/statistics-report/",
   1067       .url_suffix = "transactions",
   1068       .method = MHD_HTTP_METHOD_GET,
   1069       .permission = "statistics-read",
   1070       .handler = &TMH_private_get_statistics_report_transactions,
   1071     },
   1072     {
   1073       .url_prefix = NULL
   1074     }
   1075   };
   1076   static struct TMH_RequestHandler public_handlers[] = {
   1077     {
   1078       /* for "admin" instance, it does not even
   1079          have to exist before we give the WebUI */
   1080       .url_prefix = "/",
   1081       .method = MHD_HTTP_METHOD_GET,
   1082       .mime_type = "text/html",
   1083       .skip_instance = true,
   1084       .default_only = true,
   1085       .handler = &spa_redirect,
   1086       .response_code = MHD_HTTP_FOUND
   1087     },
   1088     {
   1089       .url_prefix = "/config",
   1090       .method = MHD_HTTP_METHOD_GET,
   1091       .skip_instance = true,
   1092       .default_only = true,
   1093       .handler = &MH_handler_config
   1094     },
   1095     {
   1096       .url_prefix = "/exchanges",
   1097       .method = MHD_HTTP_METHOD_GET,
   1098       .skip_instance = true,
   1099       .default_only = true,
   1100       .handler = &MH_handler_exchanges
   1101     },
   1102     {
   1103       /* for "normal" instance,s they must exist
   1104          before we give the WebUI */
   1105       .url_prefix = "/",
   1106       .method = MHD_HTTP_METHOD_GET,
   1107       .mime_type = "text/html",
   1108       .handler = &spa_redirect,
   1109       .response_code = MHD_HTTP_FOUND
   1110     },
   1111     {
   1112       .url_prefix = "/webui/",
   1113       .method = MHD_HTTP_METHOD_GET,
   1114       .mime_type = "text/html",
   1115       .skip_instance = true,
   1116       .have_id_segment = true,
   1117       .handler = &TMH_return_spa,
   1118       .response_code = MHD_HTTP_OK
   1119     },
   1120     {
   1121       .url_prefix = "/agpl",
   1122       .method = MHD_HTTP_METHOD_GET,
   1123       .skip_instance = true,
   1124       .handler = &TMH_MHD_handler_agpl_redirect
   1125     },
   1126     {
   1127       .url_prefix = "/agpl",
   1128       .method = MHD_HTTP_METHOD_GET,
   1129       .skip_instance = true,
   1130       .handler = &TMH_MHD_handler_agpl_redirect
   1131     },
   1132     {
   1133       .url_prefix = "/terms",
   1134       .method = MHD_HTTP_METHOD_GET,
   1135       .skip_instance = true,
   1136       .handler = &TMH_handler_terms
   1137     },
   1138     {
   1139       .url_prefix = "/privacy",
   1140       .method = MHD_HTTP_METHOD_GET,
   1141       .skip_instance = true,
   1142       .handler = &TMH_handler_privacy
   1143     },
   1144     /* Also serve the same /config per instance */
   1145     {
   1146       .url_prefix = "/config",
   1147       .method = MHD_HTTP_METHOD_GET,
   1148       .handler = &MH_handler_config
   1149     },
   1150     /* POST /orders/$ID/abort: */
   1151     {
   1152       .url_prefix = "/orders/",
   1153       .have_id_segment = true,
   1154       .url_suffix = "abort",
   1155       .method = MHD_HTTP_METHOD_POST,
   1156       .handler = &TMH_post_orders_ID_abort,
   1157       /* wallet may give us many coins to sign, allow 1 MB of upload
   1158          to set a conservative bound for sane wallets */
   1159       .max_upload = 1024 * 1024
   1160     },
   1161     /* POST /orders/$ID/claim: */
   1162     {
   1163       .url_prefix = "/orders/",
   1164       .have_id_segment = true,
   1165       .url_suffix = "claim",
   1166       .method = MHD_HTTP_METHOD_POST,
   1167       .handler = &TMH_post_orders_ID_claim,
   1168       /* the body should be pretty small, allow 1 MB of upload
   1169          to set a conservative bound for sane wallets */
   1170       .max_upload = 1024 * 1024
   1171     },
   1172     /* POST /orders/$ID/unclaim: */
   1173     {
   1174       .url_prefix = "/orders/",
   1175       .have_id_segment = true,
   1176       .url_suffix = "unclaim",
   1177       .method = MHD_HTTP_METHOD_POST,
   1178       .handler = &TMH_post_orders_ID_unclaim,
   1179       /* the body should be very small */
   1180       .max_upload = 1024
   1181     },
   1182     /* POST /orders/$ID/pay: */
   1183     {
   1184       .url_prefix = "/orders/",
   1185       .have_id_segment = true,
   1186       .url_suffix = "pay",
   1187       .method = MHD_HTTP_METHOD_POST,
   1188       .handler = &TMH_post_orders_ID_pay,
   1189       /* wallet may give us many coins to sign, allow 1 MB of upload
   1190          to set a conservative bound for sane wallets */
   1191       .max_upload = 1024 * 1024
   1192     },
   1193     /* POST /orders/$ID/paid: */
   1194     {
   1195       .url_prefix = "/orders/",
   1196       .have_id_segment = true,
   1197       .allow_deleted_instance = true,
   1198       .url_suffix = "paid",
   1199       .method = MHD_HTTP_METHOD_POST,
   1200       .handler = &TMH_post_orders_ID_paid,
   1201       /* the body should be pretty small, allow 1 MB of upload
   1202          to set a conservative bound for sane wallets */
   1203       .max_upload = 1024 * 1024
   1204     },
   1205     /* POST /orders/$ID/refund: */
   1206     {
   1207       .url_prefix = "/orders/",
   1208       .have_id_segment = true,
   1209       .allow_deleted_instance = true,
   1210       .url_suffix = "refund",
   1211       .method = MHD_HTTP_METHOD_POST,
   1212       .handler = &TMH_post_orders_ID_refund,
   1213       /* the body should be pretty small, allow 1 MB of upload
   1214          to set a conservative bound for sane wallets */
   1215       .max_upload = 1024 * 1024
   1216     },
   1217     /* GET /orders/$ID: */
   1218     {
   1219       .url_prefix = "/orders/",
   1220       .method = MHD_HTTP_METHOD_GET,
   1221       .allow_deleted_instance = true,
   1222       .have_id_segment = true,
   1223       .handler = &TMH_get_orders_ID
   1224     },
   1225     /* GET /sessions/$ID: */
   1226     {
   1227       .url_prefix = "/sessions/",
   1228       .method = MHD_HTTP_METHOD_GET,
   1229       .allow_deleted_instance = true,
   1230       .have_id_segment = true,
   1231       .handler = &TMH_get_sessions_ID
   1232     },
   1233     /* GET /static/ *: */
   1234     {
   1235       .url_prefix = "/static/",
   1236       .method = MHD_HTTP_METHOD_GET,
   1237       .have_id_segment = true,
   1238       .handler = &TMH_return_static
   1239     },
   1240     /* POST /reports/$ID/ */
   1241     {
   1242       .url_prefix = "/reports/",
   1243       .method = MHD_HTTP_METHOD_POST,
   1244       .have_id_segment = true,
   1245       .handler = &TMH_post_reports_ID,
   1246     },
   1247     /* GET /templates/$ID/: */
   1248     {
   1249       .url_prefix = "/templates/",
   1250       .method = MHD_HTTP_METHOD_GET,
   1251       .have_id_segment = true,
   1252       .handler = &TMH_get_templates_ID
   1253     },
   1254     /* GET /products/$HASH/image: */
   1255     {
   1256       .url_prefix = "/products/",
   1257       .method = MHD_HTTP_METHOD_GET,
   1258       .have_id_segment = true,
   1259       .allow_deleted_instance = true,
   1260       .url_suffix = "image",
   1261       .handler = &TMH_get_products_image
   1262     },
   1263     /* POST /templates/$ID: */
   1264     {
   1265       .url_prefix = "/templates/",
   1266       .method = MHD_HTTP_METHOD_POST,
   1267       .have_id_segment = true,
   1268       .handler = &TMH_post_using_templates_ID,
   1269       .max_upload = 1024 * 1024
   1270     },
   1271     /* POST /challenge/$ID: */
   1272     {
   1273       .url_prefix = "/challenge/",
   1274       .method = MHD_HTTP_METHOD_POST,
   1275       .have_id_segment = true,
   1276       .handler = &TMH_post_challenge_ID,
   1277       .max_upload = 1024
   1278     },
   1279     /* POST /challenge/$ID/confirm: */
   1280     {
   1281       .url_prefix = "/challenge/",
   1282       .method = MHD_HTTP_METHOD_POST,
   1283       .have_id_segment = true,
   1284       .url_suffix = "confirm",
   1285       .handler = &TMH_post_challenge_ID_confirm,
   1286       .max_upload = 1024
   1287     },
   1288     /* POST /instances */
   1289     {
   1290       .url_prefix = "/instances",
   1291       .method = MHD_HTTP_METHOD_POST,
   1292       .skip_instance = true,
   1293       .default_only = true,
   1294       .handler = &TMH_public_post_instances,
   1295       /* allow instance data of up to 8 MB, that should be plenty;
   1296          note that exceeding #GNUNET_MAX_MALLOC_CHECKED (40 MB)
   1297          would require further changes to the allocation logic
   1298          in the code... */
   1299       .max_upload = 1024 * 1024 * 8
   1300     },
   1301     /* POST /forgot-password: */
   1302     {
   1303       .url_prefix = "/forgot-password",
   1304       .method = MHD_HTTP_METHOD_POST,
   1305       .handler = &TMH_public_post_instances_ID_auth,
   1306       /* Body should be pretty small. */
   1307       .max_upload = 1024 * 1024
   1308     },
   1309 
   1310     {
   1311       .url_prefix = "*",
   1312       .method = MHD_HTTP_METHOD_OPTIONS,
   1313       .handler = &handle_server_options
   1314     },
   1315     {
   1316       .url_prefix = NULL
   1317     }
   1318   };
   1319   const char *management_prefix = "/management/";
   1320   const char *private_prefix = "/private/";
   1321   const char *url = *urlp;
   1322   struct TMH_RequestHandler *handlers;
   1323 
   1324   *is_public = false; /* ensure safe default */
   1325   if ( (0 == strncmp (url,
   1326                       management_prefix,
   1327                       strlen (management_prefix))) )
   1328   {
   1329     handlers = management_handlers;
   1330     *urlp = url + strlen (management_prefix) - 1;
   1331   }
   1332   else if ( (0 == strncmp (url,
   1333                            private_prefix,
   1334                            strlen (private_prefix))) ||
   1335             (0 == strcmp (url,
   1336                           "/private")) )
   1337   {
   1338     handlers = private_handlers;
   1339     if (0 == strcmp (url,
   1340                      "/private"))
   1341       *urlp = "/";
   1342     else
   1343       *urlp = url + strlen (private_prefix) - 1;
   1344   }
   1345   else
   1346   {
   1347     handlers = public_handlers;
   1348     *is_public = true;
   1349   }
   1350   return handlers;
   1351 }
   1352 
   1353 
   1354 /**
   1355  * Checks if the @a rh matches the given (parsed) URL.
   1356  *
   1357  * @param rh handler to compare against
   1358  * @param url the main URL (without "/private/" prefix, if any)
   1359  * @param prefix_strlen length of the prefix, i.e. 8 for '/orders/' or 7 for '/config'
   1360  * @param infix_url infix text, i.e. "$ORDER_ID".
   1361  * @param infix_strlen length of the string in @a infix_url
   1362  * @param suffix_url suffix, i.e. "/refund", including the "/"
   1363  * @param suffix_strlen number of characters in @a suffix_url
   1364  * @return true if @a rh matches this request
   1365  */
   1366 static bool
   1367 prefix_match (const struct TMH_RequestHandler *rh,
   1368               const char *url,
   1369               size_t prefix_strlen,
   1370               const char *infix_url,
   1371               size_t infix_strlen,
   1372               const char *suffix_url,
   1373               size_t suffix_strlen)
   1374 {
   1375   if ( (prefix_strlen != strlen (rh->url_prefix)) ||
   1376        (0 != memcmp (url,
   1377                      rh->url_prefix,
   1378                      prefix_strlen)) )
   1379     return false;
   1380   if (! rh->have_id_segment)
   1381   {
   1382     /* Require /$PREFIX/$SUFFIX or /$PREFIX */
   1383     if (NULL != suffix_url)
   1384       return false;       /* too many segments to match */
   1385     if ( (NULL == infix_url)   /* either or */
   1386          ^ (NULL == rh->url_suffix) )
   1387       return false;       /* suffix existence mismatch */
   1388     /* If /$PREFIX/$SUFFIX, check $SUFFIX matches */
   1389     if ( (NULL != infix_url) &&
   1390          ( (infix_strlen != strlen (rh->url_suffix)) ||
   1391            (0 != memcmp (infix_url,
   1392                          rh->url_suffix,
   1393                          infix_strlen)) ) )
   1394       return false;       /* cannot use infix as suffix: content mismatch */
   1395   }
   1396   else
   1397   {
   1398     /* Require /$PREFIX/$ID or /$PREFIX/$ID/$SUFFIX */
   1399     if (NULL == infix_url)
   1400       return false;       /* infix existence mismatch */
   1401     if ( ( (NULL == suffix_url)
   1402            ^ (NULL == rh->url_suffix) ) )
   1403       return false;       /* suffix existence mismatch */
   1404     if ( (NULL != suffix_url) &&
   1405          ( (suffix_strlen != strlen (rh->url_suffix)) ||
   1406            (0 != memcmp (suffix_url,
   1407                          rh->url_suffix,
   1408                          suffix_strlen)) ) )
   1409       return false;       /* suffix content mismatch */
   1410   }
   1411   return true;
   1412 }
   1413 
   1414 
   1415 /**
   1416  * Identify the handler of the request from the @a url and @a method
   1417  *
   1418  * @param[in,out] hc handler context to update with applicable handler
   1419  * @param handlers array of handlers to consider
   1420  * @param url URL to match against the handlers
   1421  * @param method HTTP access method to consider
   1422  * @param use_admin set to true if we are using the admin instance
   1423  * @return #GNUNET_OK on success,
   1424  *         #GNUNET_NO if an error was queued (return #MHD_YES)
   1425  *         #GNUNET_SYSERR to close the connection (return #MHD_NO)
   1426  */
   1427 static enum GNUNET_GenericReturnValue
   1428 identify_handler (struct TMH_HandlerContext *hc,
   1429                   const struct TMH_RequestHandler *handlers,
   1430                   const char *url,
   1431                   const char *method,
   1432                   bool use_admin)
   1433 {
   1434   size_t prefix_strlen;   /* i.e. 8 for "/orders/", or 7 for "/config" */
   1435   const char *infix_url = NULL;   /* i.e. "$ORDER_ID", no '/'-es */
   1436   size_t infix_strlen = 0;   /* number of characters in infix_url */
   1437   const char *suffix_url = NULL;   /* i.e. "refund", excludes '/' at the beginning */
   1438   size_t suffix_strlen = 0;   /* number of characters in suffix_url */
   1439 
   1440   if (0 == strcasecmp (method,
   1441                        MHD_HTTP_METHOD_HEAD))
   1442     method = MHD_HTTP_METHOD_GET; /* MHD will deal with the rest */
   1443   if (0 == strcmp (url,
   1444                    ""))
   1445     url = "/"; /* code below does not like empty string */
   1446 
   1447   /* parse the URL into the three different components */
   1448   {
   1449     const char *slash;
   1450 
   1451     slash = strchr (&url[1], '/');
   1452     if (NULL == slash)
   1453     {
   1454       /* the prefix was everything */
   1455       prefix_strlen = strlen (url);
   1456     }
   1457     else
   1458     {
   1459       prefix_strlen = slash - url + 1;   /* includes both '/'-es if present! */
   1460       infix_url = slash + 1;
   1461       slash = strchr (infix_url, '/');
   1462       if (NULL == slash)
   1463       {
   1464         /* the infix was the rest */
   1465         infix_strlen = strlen (infix_url);
   1466       }
   1467       else
   1468       {
   1469         infix_strlen = slash - infix_url;   /* excludes both '/'-es */
   1470         suffix_url = slash + 1;   /* skip the '/' */
   1471         suffix_strlen = strlen (suffix_url);
   1472       }
   1473       hc->infix = GNUNET_strndup (infix_url,
   1474                                   infix_strlen);
   1475     }
   1476   }
   1477 
   1478   /* find matching handler */
   1479   {
   1480     bool url_found = false;
   1481 
   1482     for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
   1483     {
   1484       const struct TMH_RequestHandler *rh = &handlers[i];
   1485 
   1486       if (rh->default_only && (! use_admin))
   1487         continue;
   1488       if (! prefix_match (rh,
   1489                           url,
   1490                           prefix_strlen,
   1491                           infix_url,
   1492                           infix_strlen,
   1493                           suffix_url,
   1494                           suffix_strlen))
   1495         continue;
   1496       url_found = true;
   1497       if (0 == strcasecmp (method,
   1498                            MHD_HTTP_METHOD_OPTIONS))
   1499       {
   1500         return (MHD_YES ==
   1501                 TALER_MHD_reply_cors_preflight (hc->connection))
   1502             ? GNUNET_NO
   1503             : GNUNET_SYSERR;
   1504       }
   1505       if ( (rh->method != NULL) &&
   1506            (0 != strcasecmp (method,
   1507                              rh->method)) )
   1508         continue;
   1509       hc->rh = rh;
   1510       break;
   1511     }
   1512     /* Handle HTTP 405: METHOD NOT ALLOWED case */
   1513     if ( (NULL == hc->rh) &&
   1514          (url_found) )
   1515     {
   1516       struct MHD_Response *reply;
   1517       enum MHD_Result ret;
   1518       char *allowed = NULL;
   1519 
   1520       GNUNET_break_op (0);
   1521       /* compute 'Allowed:' header (required by HTTP spec for 405 replies) */
   1522       for (unsigned int i = 0; NULL != handlers[i].url_prefix; i++)
   1523       {
   1524         const struct TMH_RequestHandler *rh = &handlers[i];
   1525 
   1526         if (rh->default_only && (! use_admin))
   1527           continue;
   1528         if (! prefix_match (rh,
   1529                             url,
   1530                             prefix_strlen,
   1531                             infix_url,
   1532                             infix_strlen,
   1533                             suffix_url,
   1534                             suffix_strlen))
   1535           continue;
   1536         if (NULL == allowed)
   1537         {
   1538           allowed = GNUNET_strdup (rh->method);
   1539         }
   1540         else
   1541         {
   1542           char *tmp;
   1543 
   1544           GNUNET_asprintf (&tmp,
   1545                            "%s, %s",
   1546                            allowed,
   1547                            rh->method);
   1548           GNUNET_free (allowed);
   1549           allowed = tmp;
   1550         }
   1551         if (0 == strcasecmp (rh->method,
   1552                              MHD_HTTP_METHOD_GET))
   1553         {
   1554           char *tmp;
   1555 
   1556           GNUNET_asprintf (&tmp,
   1557                            "%s, %s",
   1558                            allowed,
   1559                            MHD_HTTP_METHOD_HEAD);
   1560           GNUNET_free (allowed);
   1561           allowed = tmp;
   1562         }
   1563       }
   1564       reply = TALER_MHD_make_error (TALER_EC_GENERIC_METHOD_INVALID,
   1565                                     method);
   1566       GNUNET_break (MHD_YES ==
   1567                     MHD_add_response_header (reply,
   1568                                              MHD_HTTP_HEADER_ALLOW,
   1569                                              allowed));
   1570       GNUNET_free (allowed);
   1571       ret = MHD_queue_response (hc->connection,
   1572                                 MHD_HTTP_METHOD_NOT_ALLOWED,
   1573                                 reply);
   1574       MHD_destroy_response (reply);
   1575       return (MHD_YES == ret)
   1576           ? GNUNET_NO
   1577           : GNUNET_SYSERR;
   1578     }
   1579     if (NULL == hc->rh)
   1580     {
   1581       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
   1582                   "Endpoint `%s' not known\n",
   1583                   hc->url);
   1584       return (MHD_YES ==
   1585               TALER_MHD_reply_with_error (hc->connection,
   1586                                           MHD_HTTP_NOT_FOUND,
   1587                                           TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
   1588                                           hc->url))
   1589           ? GNUNET_NO
   1590           : GNUNET_SYSERR;
   1591     }
   1592   }
   1593   return GNUNET_OK;
   1594 }
   1595 
   1596 
   1597 enum GNUNET_GenericReturnValue
   1598 TMH_dispatch_request (struct TMH_HandlerContext *hc,
   1599                       const char *url,
   1600                       const char *method,
   1601                       bool use_admin,
   1602                       bool *is_public)
   1603 {
   1604   const struct TMH_RequestHandler *handlers;
   1605 
   1606   *is_public = false;
   1607   handlers = determine_handler_group (&url,
   1608                                       is_public);
   1609   return identify_handler (hc,
   1610                            handlers,
   1611                            url,
   1612                            method,
   1613                            use_admin);
   1614 }