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