merchant

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

taler-merchant-httpd_dispatcher.c (52381B)


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