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