taler_mhd_lib.h (44557B)
1 /* 2 This file is part of TALER 3 Copyright (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 Affero General Public License for more details. 12 13 You should have received a copy of the GNU Affero General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file taler_mhd_lib.h 18 * @brief API for generating MHD replies 19 * @author Florian Dold 20 * @author Benedikt Mueller 21 * @author Christian Grothoff 22 */ 23 #ifndef TALER_MHD_LIB_H 24 #define TALER_MHD_LIB_H 25 26 #include <jansson.h> 27 #include <microhttpd.h> 28 #include <gnunet/gnunet_json_lib.h> 29 #include <taler/taler_error_codes.h> 30 #include <taler/taler_util.h> 31 32 33 /** 34 * Maximum POST request size. 35 */ 36 #define TALER_MHD_REQUEST_BUFFER_MAX (1024 * 1024 * 16) 37 38 39 /** 40 * Global options for response generation. 41 */ 42 enum TALER_MHD_GlobalOptions 43 { 44 45 /** 46 * Use defaults. 47 */ 48 TALER_MHD_GO_NONE = 0, 49 50 /** 51 * Add "Connection: Close" header. 52 */ 53 TALER_MHD_GO_FORCE_CONNECTION_CLOSE = 1, 54 55 /** 56 * Disable use of compression, even if the client 57 * supports it. 58 */ 59 TALER_MHD_GO_DISABLE_COMPRESSION = 2 60 61 }; 62 63 64 #if MHD_VERSION < 0x00097701 65 #define MHD_create_response_from_buffer_static(s, b) \ 66 MHD_create_response_from_buffer (s, \ 67 (const char *) b, \ 68 MHD_RESPMEM_PERSISTENT) 69 #endif 70 71 72 /** 73 * Find out if an MHD connection is using HTTPS (either 74 * directly or via proxy). 75 * 76 * WARNING: if present, the client-supplied "X-Forwarded-Proto" 77 * request header takes precedence over what MHD knows about the 78 * connection, and there is no trusted-proxy configuration. Thus the 79 * answer is attacker-controlled whenever clients can reach the daemon 80 * directly, and must not be used for access-control decisions. 81 * 82 * @param connection MHD connection 83 * @returns #GNUNET_YES if the MHD connection is using https, 84 * #GNUNET_NO if the MHD connection is using http, 85 * #GNUNET_SYSERR if the connection type couldn't be determined 86 */ 87 enum GNUNET_GenericReturnValue 88 TALER_mhd_is_https (struct MHD_Connection *connection); 89 90 91 /** 92 * Convert query argument to @a yna value. 93 * 94 * @param connection connection to take query argument from 95 * @param arg argument to try for 96 * @param default_val value to assign if the argument is not present 97 * @param[out] yna value to set 98 * @return true on success, false if the parameter was malformed 99 */ 100 bool 101 TALER_MHD_arg_to_yna (struct MHD_Connection *connection, 102 const char *arg, 103 enum TALER_EXCHANGE_YesNoAll default_val, 104 enum TALER_EXCHANGE_YesNoAll *yna); 105 106 107 /** 108 * Convert query argument to @a b boolean value. 109 * 110 * @param connection connection to take query argument from 111 * @param arg argument to try for 112 * @param default_val value to assign if the argument is not present 113 * @param[out] b value to set 114 * @return true on success, false if the parameter was malformed 115 */ 116 bool 117 TALER_MHD_arg_to_bool (struct MHD_Connection *connection, 118 const char *arg, 119 bool default_val, 120 bool *b); 121 122 123 /** 124 * Set global options for response generation within libtalermhd. 125 * 126 * @param go global options to use 127 */ 128 void 129 TALER_MHD_setup (enum TALER_MHD_GlobalOptions go); 130 131 132 /** 133 * Add headers we want to return in every response. Useful for testing, like 134 * if we want to always close connections. 135 * 136 * @param response response to modify 137 * @param allow_store set to true to NOT add a "Cache-Control" 138 * directive that prevents caches and browsers from storing the data; 139 * if false, we set "Cache-Control: no-store" (privacy by default); 140 * set to true if the response contains no personal data 141 */ 142 void 143 TALER_MHD_add_global_headers (struct MHD_Response *response, 144 bool allow_store); 145 146 147 /** 148 * Try to compress a response body. Updates @a buf and @a buf_size. 149 * 150 * @param[in,out] buf pointer to body to compress 151 * @param[in,out] buf_size pointer to initial size of @a buf 152 * @return #MHD_YES if @a buf was compressed 153 */ 154 enum MHD_Result 155 TALER_MHD_body_compress (void **buf, 156 size_t *buf_size); 157 158 159 /** 160 * List of compression types we check for. Larger numeric values 161 * indicate a preferred algorithm. 162 */ 163 enum TALER_MHD_CompressionType 164 { 165 /** 166 * Compression is not supported. 167 */ 168 TALER_MHD_CT_NONE = 0, 169 170 /** 171 * Deflate compression supported. 172 */ 173 TALER_MHD_CT_DEFLATE, 174 175 /** 176 * gzip compression supported. 177 */ 178 TALER_MHD_CT_GZIP, 179 180 /** 181 * zstd compression supported. 182 */ 183 TALER_MHD_CT_ZSTD, 184 185 /** 186 * End of list marker. 187 */ 188 TALER_MHD_CT_MAX 189 }; 190 191 192 /** 193 * What type of HTTP compression is supported by the client? 194 * 195 * @param connection connection to check 196 * @param max maximum compression level to check for 197 * @return #MHD_YES if 'deflate' compression is allowed 198 */ 199 enum TALER_MHD_CompressionType 200 TALER_MHD_can_compress (struct MHD_Connection *connection, 201 enum TALER_MHD_CompressionType max); 202 203 204 /** 205 * Send JSON object as response. 206 * 207 * @param connection the MHD connection 208 * @param json the json object 209 * @param response_code the http response code 210 * @return MHD result code 211 */ 212 enum MHD_Result 213 TALER_MHD_reply_json (struct MHD_Connection *connection, 214 const json_t *json, 215 unsigned int response_code); 216 217 218 /** 219 * Send JSON object as response, and free the @a json 220 * object. 221 * 222 * @param connection the MHD connection 223 * @param json the json object (freed!) 224 * @param response_code the http response code 225 * @return MHD result code 226 */ 227 enum MHD_Result 228 TALER_MHD_reply_json_steal (struct MHD_Connection *connection, 229 json_t *json, 230 unsigned int response_code); 231 232 233 /** 234 * Function to call to handle the request by building a JSON 235 * reply from varargs. 236 * 237 * @param connection the MHD connection to handle 238 * @param response_code HTTP response code to use 239 * @param ... varargs of JSON pack specification 240 * @return MHD result code 241 */ 242 #define TALER_MHD_REPLY_JSON_PACK(connection,response_code,...) \ 243 TALER_MHD_reply_json_steal (connection, GNUNET_JSON_PACK (__VA_ARGS__), \ 244 response_code) 245 246 247 /** 248 * Send a response indicating an error. 249 * 250 * @param connection the MHD connection to use 251 * @param http_status HTTP status code to use 252 * @param ec error code uniquely identifying the error 253 * @param detail additional optional detail about the error 254 * @return a MHD result code 255 */ 256 enum MHD_Result 257 TALER_MHD_reply_with_error (struct MHD_Connection *connection, 258 unsigned int http_status, 259 enum TALER_ErrorCode ec, 260 const char *detail); 261 262 263 /** 264 * Send a response indicating an error. The HTTP status code is 265 * to be derived from the @a ec. 266 * 267 * @param connection the MHD connection to use 268 * @param ec error code uniquely identifying the error 269 * @param detail additional optional detail about the error 270 * @return a MHD result code 271 */ 272 enum MHD_Result 273 TALER_MHD_reply_with_ec (struct MHD_Connection *connection, 274 enum TALER_ErrorCode ec, 275 const char *detail); 276 277 278 /** 279 * Produce HTTP "Date:" header. 280 * 281 * @param at time to write to @a date 282 * @param[out] date where to write the header, with 283 * at least 128 bytes available space. 284 */ 285 void 286 TALER_MHD_get_date_string (struct GNUNET_TIME_Absolute at, 287 char date[128]); 288 289 290 /** 291 * Make JSON response object. 292 * 293 * @param json the json object 294 * @return MHD response object 295 */ 296 struct MHD_Response * 297 TALER_MHD_make_json (const json_t *json); 298 299 300 /** 301 * Make JSON response object and free @a json. 302 * 303 * @param json the json object, freed. 304 * @return MHD response object 305 */ 306 struct MHD_Response * 307 TALER_MHD_make_json_steal (json_t *json); 308 309 310 /** 311 * Make JSON response object. 312 * 313 * @param ... varargs 314 * @return MHD response object 315 */ 316 #define TALER_MHD_MAKE_JSON_PACK(...) \ 317 TALER_MHD_make_json_steal (GNUNET_JSON_PACK (__VA_ARGS__)) 318 319 320 /** 321 * Pack Taler error code @a ec and associated hint into a 322 * JSON object. 323 * 324 * @param ec error code to pack 325 * @return packer array entries (two!) 326 */ 327 #define TALER_MHD_PACK_EC(ec) \ 328 GNUNET_JSON_pack_uint64 ("code", ec), \ 329 GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec)) 330 331 /** 332 * Create a response indicating an internal error. 333 * 334 * @param ec error code to return 335 * @param detail additional optional detail about the error, can be NULL 336 * @return a MHD response object 337 */ 338 struct MHD_Response * 339 TALER_MHD_make_error (enum TALER_ErrorCode ec, 340 const char *detail); 341 342 343 /** 344 * Send a response indicating that the request was too big. 345 * 346 * @param connection the MHD connection to use 347 * @return a MHD result code 348 */ 349 enum MHD_Result 350 TALER_MHD_reply_request_too_large (struct MHD_Connection *connection); 351 352 353 /** 354 * Function to call to handle the request by sending 355 * back a redirect to the AGPL source code. 356 * 357 * @param connection the MHD connection to handle 358 * @param url where to redirect for the sources 359 * @return MHD result code 360 */ 361 enum MHD_Result 362 TALER_MHD_reply_agpl (struct MHD_Connection *connection, 363 const char *url); 364 365 366 /** 367 * Function to call to handle the request by sending 368 * back static data. 369 * 370 * @param connection the MHD connection to handle 371 * @param http_status status code to return 372 * @param mime_type content-type to use 373 * @param body response payload 374 * @param body_size number of bytes in @a body 375 * @return MHD result code 376 */ 377 enum MHD_Result 378 TALER_MHD_reply_static (struct MHD_Connection *connection, 379 unsigned int http_status, 380 const char *mime_type, 381 const char *body, 382 size_t body_size); 383 384 385 /** 386 * Process a POST request containing a JSON object. This 387 * function realizes an MHD POST processor that will 388 * (incrementally) process JSON data uploaded to the HTTP 389 * server. It will store the required state in the 390 * "connection_cls", which must be cleaned up using 391 * #TALER_MHD_parse_post_cleanup_callback(). 392 * 393 * @param connection the MHD connection 394 * @param con_cls the closure (points to a `struct Buffer *`) 395 * @param upload_data the POST data 396 * @param upload_data_size number of bytes in @a upload_data 397 * @param json the JSON object for a completed request 398 * @return 399 * #GNUNET_YES if json object was parsed or at least 400 * may be parsed in the future (call again); 401 * `*json` will be NULL if we need to be called again, 402 * and non-NULL if we are done. 403 * #GNUNET_NO is request incomplete or invalid 404 * (error message was generated) 405 * #GNUNET_SYSERR on internal error 406 * (we could not even queue an error message, 407 * close HTTP session with MHD_NO) 408 */ 409 enum GNUNET_GenericReturnValue 410 TALER_MHD_parse_post_json (struct MHD_Connection *connection, 411 void **con_cls, 412 const char *upload_data, 413 size_t *upload_data_size, 414 json_t **json); 415 416 417 /** 418 * Function called whenever we are done with a request 419 * to clean up our state. 420 * 421 * @param con_cls value as it was left by 422 * #TALER_MHD_parse_post_json(), to be cleaned up 423 */ 424 void 425 TALER_MHD_parse_post_cleanup_callback (void *con_cls); 426 427 428 /** 429 * Parse JSON object into components based on the given field 430 * specification. If parsing fails, we return an HTTP 431 * status code of 400 (#MHD_HTTP_BAD_REQUEST). 432 * 433 * @param connection the connection to send an error response to 434 * @param root the JSON node to start the navigation at. 435 * @param spec field specification for the parser 436 * @return 437 * #GNUNET_YES if navigation was successful (caller is responsible 438 * for freeing allocated variable-size data using 439 * GNUNET_JSON_parse_free() when done) 440 * #GNUNET_NO if json is malformed, error response was generated 441 * #GNUNET_SYSERR on internal error 442 */ 443 enum GNUNET_GenericReturnValue 444 TALER_MHD_parse_json_data (struct MHD_Connection *connection, 445 const json_t *root, 446 struct GNUNET_JSON_Specification *spec); 447 448 449 /** 450 * Parse JSON object that we (the server!) generated into components based on 451 * the given field specification. The difference to 452 * #TALER_MHD_parse_json_data() is that this function will fail 453 * with an HTTP failure of 500 (internal server error) in case 454 * parsing fails, instead of blaming it on the client with a 455 * 400 (#MHD_HTTP_BAD_REQUEST). 456 * 457 * @param connection the connection to send an error response to 458 * @param root the JSON node to start the navigation at. 459 * @param spec field specification for the parser 460 * @return 461 * #GNUNET_YES if navigation was successful (caller is responsible 462 * for freeing allocated variable-size data using 463 * GNUNET_JSON_parse_free() when done) 464 * #GNUNET_NO if json is malformed, error response was generated 465 * #GNUNET_SYSERR on internal error 466 */ 467 enum GNUNET_GenericReturnValue 468 TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection, 469 const json_t *root, 470 struct GNUNET_JSON_Specification *spec); 471 472 473 /** 474 * Parse JSON array into components based on the given field 475 * specification. Generates error response on parse errors. 476 * 477 * @param connection the connection to send an error response to 478 * @param root the JSON node to start the navigation at. 479 * @param[in,out] spec field specification for the parser 480 * @param ... -1-terminated list of array offsets of type 'int' 481 * @return 482 * #GNUNET_YES if navigation was successful (caller is responsible 483 * for freeing allocated variable-size data using 484 * GNUNET_JSON_parse_free() when done) 485 * #GNUNET_NO if json is malformed, error response was generated 486 * #GNUNET_SYSERR on internal error 487 */ 488 enum GNUNET_GenericReturnValue 489 TALER_MHD_parse_json_array (struct MHD_Connection *connection, 490 const json_t *root, 491 struct GNUNET_JSON_Specification *spec, 492 ...); 493 494 495 /** 496 * Extract optional relative time argument from request. 497 * 498 * @param connection the MHD connection 499 * @param label name of the argument to parse 500 * @param[out] duration set to #GNUNET_TIME_UNIT_ZERO if there was no duration argument given 501 * @return #GNUNET_OK on success, #GNUNET_NO if an 502 * error was returned on @a connection (caller should return #MHD_YES) and 503 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 504 */ 505 enum GNUNET_GenericReturnValue 506 TALER_MHD_parse_request_arg_rel_time (struct MHD_Connection *connection, 507 const char *label, 508 struct GNUNET_TIME_Relative *duration); 509 510 511 /** 512 * Extract optional relative time argument from request. 513 * Macro that *returns* #MHD_YES/#MHD_NO if the @a label 514 * argument existed but failed to parse. 515 * 516 * @param connection the MHD connection 517 * @param label label to check for 518 * @param[out] duration set to #GNUNET_TIME_UNIT_ZERO if there was no duration given 519 */ 520 #define TALER_MHD_parse_request_rel_time(connection,label,duration) \ 521 do { \ 522 switch (TALER_MHD_parse_request_arg_rel_time (connection, \ 523 label, \ 524 duration)) \ 525 { \ 526 case GNUNET_SYSERR: \ 527 GNUNET_break (0); \ 528 return MHD_NO; \ 529 case GNUNET_NO: \ 530 GNUNET_break_op (0); \ 531 return MHD_YES; \ 532 case GNUNET_OK: \ 533 break; \ 534 } \ 535 } while (0) 536 537 538 /** 539 * Extract optional "timeout_ms" argument from request. 540 * 541 * @param connection the MHD connection 542 * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, 543 * the current time plus the value given under "timeout_ms" otherwise, 544 * where values above 30 minutes are treated as 30 minutes 545 * @return #GNUNET_OK on success, #GNUNET_NO if an 546 * error was returned on @a connection (caller should return #MHD_YES) and 547 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 548 */ 549 enum GNUNET_GenericReturnValue 550 TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection, 551 struct GNUNET_TIME_Absolute *expiration); 552 553 554 /** 555 * Extract optional "timeout_ms" argument from request. 556 * Macro that *returns* #MHD_YES/#MHD_NO if the "timeout_ms" 557 * argument existed but failed to parse. 558 * 559 * @param connection the MHD connection 560 * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, 561 * the current time plus the value given under "timeout_ms" otherwise, 562 * where values above 30 minutes are treated as 30 minutes 563 */ 564 #define TALER_MHD_parse_request_timeout(connection,expiration) \ 565 do { \ 566 switch (TALER_MHD_parse_request_arg_timeout (connection, \ 567 expiration)) \ 568 { \ 569 case GNUNET_SYSERR: \ 570 GNUNET_break (0); \ 571 return MHD_NO; \ 572 case GNUNET_NO: \ 573 GNUNET_break_op (0); \ 574 return MHD_YES; \ 575 case GNUNET_OK: \ 576 break; \ 577 } \ 578 } while (0) 579 580 581 /** 582 * Extract optional timestamp argument from request. 583 * 584 * @param connection the MHD connection 585 * @param fname name of the argument to parse 586 * @param[in,out] ts set to the timestamp given in the request; 587 * left unchanged if the argument was not present, so the 588 * caller must initialize @a ts to the desired default 589 * @return #GNUNET_OK on success, #GNUNET_NO if an 590 * error was returned on @a connection (caller should return #MHD_YES) and 591 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 592 */ 593 enum GNUNET_GenericReturnValue 594 TALER_MHD_parse_request_arg_timestamp (struct MHD_Connection *connection, 595 const char *fname, 596 struct GNUNET_TIME_Timestamp *ts); 597 598 599 /** 600 * Extract optional timestamp argument from request. 601 * Macro that *returns* #MHD_YES/#MHD_NO if the timestamp 602 * argument existed but failed to parse. 603 * 604 * @param connection the MHD connection 605 * @param fname name of the argument 606 * @param[in,out] ts set to the timestamp given in the request; 607 * left unchanged if the argument was not present, so the 608 * caller must initialize @a ts to the desired default 609 */ 610 #define TALER_MHD_parse_request_timestamp(connection,fname,ts) \ 611 do { \ 612 switch (TALER_MHD_parse_request_arg_timestamp (connection, \ 613 fname, \ 614 ts)) \ 615 { \ 616 case GNUNET_SYSERR: \ 617 GNUNET_break (0); \ 618 return MHD_NO; \ 619 case GNUNET_NO: \ 620 GNUNET_break_op (0); \ 621 return MHD_YES; \ 622 case GNUNET_OK: \ 623 break; \ 624 } \ 625 } while (0) 626 627 628 /** 629 * Extract optional "yes/no/all" argument from request. 630 * Macro that *returns* #MHD_YES/#MHD_NO if the 631 * argument existed but failed to parse. 632 * 633 * @param connection the MHD connection 634 * @param name name of the query parameter to parse 635 * @param def default value to set if absent 636 * @param[out] ret set to the yes/no/all value 637 */ 638 #define TALER_MHD_parse_request_yna(connection,name,def,ret) \ 639 do { \ 640 if (! (TALER_MHD_arg_to_yna (connection, \ 641 name, \ 642 def, \ 643 ret)) ) \ 644 { \ 645 GNUNET_break_op (0); \ 646 return TALER_MHD_reply_with_error ( \ 647 connection, \ 648 MHD_HTTP_BAD_REQUEST, \ 649 TALER_EC_GENERIC_PARAMETER_MALFORMED, \ 650 name); \ 651 } \ 652 } while (0) 653 654 655 /** 656 * Extract optional "yes/no" argument from request. 657 * Macro that *returns* #MHD_YES/#MHD_NO if the 658 * argument existed but failed to parse. 659 * 660 * @param connection the MHD connection 661 * @param name name of the query parameter to parse 662 * @param def default value to set if absent 663 * @param[out] ret set to the yes/no/all value 664 */ 665 #define TALER_MHD_parse_request_bool(connection,name,def,ret) \ 666 do { \ 667 if (! (TALER_MHD_arg_to_bool (connection, \ 668 name, \ 669 def, \ 670 ret)) ) \ 671 { \ 672 GNUNET_break_op (0); \ 673 return TALER_MHD_reply_with_error ( \ 674 connection, \ 675 MHD_HTTP_BAD_REQUEST, \ 676 TALER_EC_GENERIC_PARAMETER_MALFORMED, \ 677 name); \ 678 } \ 679 } while (0) 680 681 682 /** 683 * Extract optional numeric limit argument from request. 684 * 685 * @param connection the MHD connection 686 * @param name name of the query parameter 687 * @param[out] off set to the offset, unchanged if the 688 * option was not given 689 * @return #GNUNET_OK on success, 690 * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and 691 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 692 */ 693 enum GNUNET_GenericReturnValue 694 TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection, 695 const char *name, 696 uint64_t *off); 697 698 699 /** 700 * Extract optional numeric argument from request. 701 * Macro that *returns* #MHD_YES/#MHD_NO if the 702 * requested argument existed but failed to parse. 703 * 704 * @param connection the MHD connection 705 * @param name name of the argument to parse 706 * @param[out] off set to the given numeric value, 707 * unchanged if value was not specified 708 */ 709 #define TALER_MHD_parse_request_number(connection,name,off) \ 710 do { \ 711 switch (TALER_MHD_parse_request_arg_number (connection, \ 712 name, \ 713 off)) \ 714 { \ 715 case GNUNET_SYSERR: \ 716 GNUNET_break (0); \ 717 return MHD_NO; \ 718 case GNUNET_NO: \ 719 GNUNET_break_op (0); \ 720 return MHD_YES; \ 721 case GNUNET_OK: \ 722 break; \ 723 } \ 724 } while (0) 725 726 727 /** 728 * Extract optional signed numeric limit argument from request. 729 * 730 * @param connection the MHD connection 731 * @param name name of the query parameter 732 * @param[out] val set to the signed value, unchanged if the 733 * option was not given 734 * @return #GNUNET_OK on success, 735 * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and 736 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 737 */ 738 enum GNUNET_GenericReturnValue 739 TALER_MHD_parse_request_arg_snumber (struct MHD_Connection *connection, 740 const char *name, 741 int64_t *val); 742 743 744 /** 745 * Extract optional numeric argument from request. 746 * Macro that *returns* #MHD_YES/#MHD_NO if the 747 * requested argument existed but failed to parse. 748 * 749 * @param connection the MHD connection 750 * @param name name of the argument to parse 751 * @param[out] val set to the given numeric value, 752 * unchanged if value was not specified 753 */ 754 #define TALER_MHD_parse_request_snumber(connection,name,val) \ 755 do { \ 756 switch (TALER_MHD_parse_request_arg_snumber (connection, \ 757 name, \ 758 val)) \ 759 { \ 760 case GNUNET_SYSERR: \ 761 GNUNET_break (0); \ 762 return MHD_NO; \ 763 case GNUNET_NO: \ 764 GNUNET_break_op (0); \ 765 return MHD_YES; \ 766 case GNUNET_OK: \ 767 break; \ 768 } \ 769 } while (0) 770 771 772 /** 773 * Extract optional amount argument from request. 774 * 775 * Note that the amount is parsed in any currency; the caller 776 * MUST check that the resulting currency is the one it expects 777 * (say using #TALER_amount_is_currency()) before using @a val. 778 * 779 * @param connection the MHD connection 780 * @param name name of the query parameter 781 * @param[out] val set to the amount, unchanged if the 782 * option was not given 783 * @return #GNUNET_OK on success, 784 * #GNUNET_NO if an error was returned on @a connection (caller should return #MHD_YES) and 785 * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) 786 */ 787 enum GNUNET_GenericReturnValue 788 TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection, 789 const char *name, 790 struct TALER_Amount *val); 791 792 793 /** 794 * Extract optional amount argument from request. Macro that *returns* 795 * #MHD_YES/#MHD_NO if the requested argument existed but failed to parse. 796 * 797 * @param connection the MHD connection 798 * @param name name of the argument to parse 799 * @param[out] val set to the given amount, 800 * unchanged if value was not specified 801 */ 802 #define TALER_MHD_parse_request_amount(connection,name,val) \ 803 do { \ 804 switch (TALER_MHD_parse_request_arg_amount (connection, \ 805 name, \ 806 val)) \ 807 { \ 808 case GNUNET_SYSERR: \ 809 GNUNET_break (0); \ 810 return MHD_NO; \ 811 case GNUNET_NO: \ 812 GNUNET_break_op (0); \ 813 return MHD_YES; \ 814 case GNUNET_OK: \ 815 break; \ 816 } \ 817 } while (0) 818 819 820 /** 821 * Extract fixed-size base32crockford encoded data from request argument. 822 * 823 * Queues an error response to the connection if the parameter is missing or 824 * invalid. 825 * 826 * @param connection the MHD connection 827 * @param param_name the name of the parameter with the key 828 * @param[out] out_data pointer to store the result 829 * @param out_size expected size of @a out_data 830 * @param[out] present set to true if argument was found 831 * @return 832 * #GNUNET_YES if the the argument is present 833 * #GNUNET_NO if the argument is malformed 834 * #GNUNET_SYSERR on internal error (error response could not be sent) 835 */ 836 enum GNUNET_GenericReturnValue 837 TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, 838 const char *param_name, 839 void *out_data, 840 size_t out_size, 841 bool *present); 842 843 844 /** 845 * Extract fixed-size base32crockford encoded data from request header. 846 * 847 * Queues an error response to the connection if the parameter is missing or 848 * invalid. 849 * 850 * @param connection the MHD connection 851 * @param header_name the name of the HTTP header with the value 852 * @param[out] out_data pointer to store the result 853 * @param out_size expected size of @a out_data 854 * @param[out] present set to true if argument was found 855 * @return 856 * #GNUNET_YES if the the argument is present 857 * #GNUNET_NO if the argument is malformed 858 * #GNUNET_SYSERR on internal error (error response could not be sent) 859 */ 860 enum GNUNET_GenericReturnValue 861 TALER_MHD_parse_request_header_data (struct MHD_Connection *connection, 862 const char *header_name, 863 void *out_data, 864 size_t out_size, 865 bool *present); 866 867 /** 868 * Extract fixed-size base32crockford encoded data from request. 869 * 870 * @param connection the MHD connection 871 * @param name the name of the parameter with the key 872 * @param[out] val pointer to store the result, type must determine size 873 * @param[in,out] required pass true to require presence of this argument; if 'false' 874 * set to true if the argument was found 875 * @return 876 * #GNUNET_YES if the the argument is present 877 * #GNUNET_NO if the argument is absent or malformed 878 * #GNUNET_SYSERR on internal error (error response could not be sent) 879 */ 880 #define TALER_MHD_parse_request_arg_auto(connection,name,val,required) \ 881 do { \ 882 bool p; \ 883 switch (TALER_MHD_parse_request_arg_data (connection, name, \ 884 val, sizeof (*val), &p)) \ 885 { \ 886 case GNUNET_SYSERR: \ 887 GNUNET_break (0); \ 888 return MHD_NO; \ 889 case GNUNET_NO: \ 890 GNUNET_break_op (0); \ 891 return MHD_YES; \ 892 case GNUNET_OK: \ 893 if (required & (! p)) \ 894 { \ 895 GNUNET_break_op (0); \ 896 return TALER_MHD_reply_with_error ( \ 897 connection, \ 898 MHD_HTTP_BAD_REQUEST, \ 899 TALER_EC_GENERIC_PARAMETER_MISSING, \ 900 name); \ 901 } \ 902 required = p; \ 903 break; \ 904 } \ 905 } while (0) 906 907 908 /** 909 * Extract required fixed-size base32crockford encoded data from request. 910 * 911 * @param connection the MHD connection 912 * @param name the name of the parameter with the key 913 * @param[out] val pointer to store the result, type must determine size 914 * @return 915 * #GNUNET_YES if the the argument is present 916 * #GNUNET_NO if the argument is absent or malformed 917 * #GNUNET_SYSERR on internal error (error response could not be sent) 918 */ 919 #define TALER_MHD_parse_request_arg_auto_t(connection,name,val) \ 920 do { \ 921 bool b = true; \ 922 TALER_MHD_parse_request_arg_auto (connection,name,val,b); \ 923 } while (0) 924 925 /** 926 * Extract fixed-size base32crockford encoded data from request. 927 * 928 * @param connection the MHD connection 929 * @param name the name of the header with the key 930 * @param[out] val pointer to store the result, type must determine size 931 * @param[in,out] required pass true to require presence of this argument; if 'false' 932 * set to true if the argument was found 933 * @return 934 * #GNUNET_YES if the the argument is present 935 * #GNUNET_NO if the argument is absent or malformed 936 * #GNUNET_SYSERR on internal error (error response could not be sent) 937 */ 938 #define TALER_MHD_parse_request_header_auto(connection,name,val,required) \ 939 do { \ 940 bool p; \ 941 switch (TALER_MHD_parse_request_header_data (connection, name, \ 942 val, sizeof (*val), \ 943 &p)) \ 944 { \ 945 case GNUNET_SYSERR: \ 946 GNUNET_break (0); \ 947 return MHD_NO; \ 948 case GNUNET_NO: \ 949 GNUNET_break_op (0); \ 950 return MHD_YES; \ 951 case GNUNET_OK: \ 952 if (required & (! p)) \ 953 return TALER_MHD_reply_with_error ( \ 954 connection, \ 955 MHD_HTTP_BAD_REQUEST, \ 956 TALER_EC_GENERIC_PARAMETER_MISSING, \ 957 name); \ 958 required = p; \ 959 break; \ 960 } \ 961 } while (0) 962 963 964 /** 965 * Extract required fixed-size base32crockford encoded data from request. 966 * 967 * @param connection the MHD connection 968 * @param name the name of the header with the key 969 * @param[out] val pointer to store the result, type must determine size 970 * @return 971 * #GNUNET_YES if the the argument is present 972 * #GNUNET_NO if the argument is absent or malformed 973 * #GNUNET_SYSERR on internal error (error response could not be sent) 974 */ 975 #define TALER_MHD_parse_request_header_auto_t(connection,name,val) \ 976 do { \ 977 bool b = true; \ 978 TALER_MHD_parse_request_header_auto (connection,name,val,b); \ 979 } while (0) 980 981 982 /** 983 * Check that the 'Content-Length' header is giving 984 * a length below @a max_len. If not, return an 985 * appropriate error response and return the 986 * correct #MHD_YES/#MHD_NO value from this function. 987 * 988 * @param connection the MHD connection 989 * @param max_len maximum allowed content length 990 * @return 991 * #GNUNET_YES if the the argument is present and below @a max_len 992 * #GNUNET_NO if the argument is absent or malformed 993 * #GNUNET_SYSERR on internal error (error response could not be sent) 994 */ 995 enum GNUNET_GenericReturnValue 996 TALER_MHD_check_content_length_ (struct MHD_Connection *connection, 997 unsigned long long max_len); 998 999 1000 /** 1001 * Check that the 'Content-Length' header is giving 1002 * a length below @a max_len. If not, return an 1003 * appropriate error response and return the 1004 * correct #MHD_YES/#MHD_NO value from this function. 1005 * 1006 * @param connection the MHD connection 1007 * @param max_len maximum allowed content length 1008 */ 1009 #define TALER_MHD_check_content_length(connection,max_len) \ 1010 do { \ 1011 switch (TALER_MHD_check_content_length_ (connection, max_len)) \ 1012 { \ 1013 case GNUNET_SYSERR: \ 1014 GNUNET_break (0); \ 1015 return MHD_NO; \ 1016 case GNUNET_NO: \ 1017 GNUNET_break_op (0); \ 1018 return MHD_YES; \ 1019 case GNUNET_OK: \ 1020 break; \ 1021 } \ 1022 } while (0) 1023 1024 1025 /** 1026 * Function called for logging by MHD. 1027 * 1028 * @param cls closure, NULL 1029 * @param fm format string (`printf()`-style) 1030 * @param ap arguments to @a fm 1031 */ 1032 void 1033 TALER_MHD_handle_logs (void *cls, 1034 const char *fm, 1035 va_list ap); 1036 1037 1038 /** 1039 * Function called on each successfully bound listen 1040 * socket by #TALER_MHD_listen_bind(). 1041 * 1042 * @param cls closure 1043 * @param fd bound listen socket (must be used and eventually 1044 * closed by the callee). Never -1. 1045 */ 1046 typedef void 1047 (*TALER_MHD_ListenSocketCallback)(void *cls, 1048 int fd); 1049 1050 1051 /** 1052 * Bind a listen socket to the UNIX domain path, 1053 * or the TCP port(s) and IP address(es) configured, 1054 * or return to @a cb the inherited sockets from systemd, 1055 * all depending on what was specified in @a cfg in 1056 * the section named @a section. 1057 * 1058 * @param cfg configuration to parse 1059 * @param section configuration section to use 1060 * @param cb function to call with each bound socket 1061 * @param cb_cls closure for @a cb 1062 * @return #GNUNET_OK on success (all configured sockets were bound) 1063 * #GNUNET_NO if some configured binding failed but the config is OK, 1064 * note that some listen sockets may have been created 1065 * #GNUNET_SYSERR if the configuration is invalid 1066 */ 1067 enum GNUNET_GenericReturnValue 1068 TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg, 1069 const char *section, 1070 TALER_MHD_ListenSocketCallback cb, 1071 void *cb_cls); 1072 1073 1074 /** 1075 * Start to run an event loop for @a daemon. As the event loop uses 1076 * select(), this also lowers the soft limit on open file descriptors 1077 * (RLIMIT_NOFILE) of the process to FD_SETSIZE. Callers should set 1078 * an MHD_OPTION_CONNECTION_LIMIT that leaves room below FD_SETSIZE 1079 * for the descriptors they use besides those of MHD's connections. 1080 * 1081 * @param daemon the MHD service to run 1082 */ 1083 void 1084 TALER_MHD_daemon_start (struct MHD_Daemon *daemon); 1085 1086 1087 /** 1088 * Stop running the event loops of all MHD daemons. 1089 */ 1090 void 1091 TALER_MHD_daemons_halt (void); 1092 1093 1094 /** 1095 * Stop accepting new connections on all MHD daemons 1096 * (and close the listen sockets). 1097 */ 1098 void 1099 TALER_MHD_daemons_quiesce (void); 1100 1101 1102 /** 1103 * Destroy all state associated with all MHD daemons. 1104 */ 1105 void 1106 TALER_MHD_daemons_destroy (void); 1107 1108 /** 1109 * Trigger all MHD daemons that were running. Needed when 1110 * a connection was resumed. 1111 */ 1112 void 1113 TALER_MHD_daemon_trigger (void); 1114 1115 1116 /** 1117 * Prepared responses for legal documents 1118 * (terms of service, privacy policy). 1119 */ 1120 struct TALER_MHD_Legal; 1121 1122 1123 /** 1124 * Load set of legal documents as specified in @a cfg in section @a section 1125 * where the Etag is given under the @a tagoption and the directory under 1126 * the @a diroption. 1127 * 1128 * @param cfg configuration to use 1129 * @param section section to load values from 1130 * @param diroption name of the option with the 1131 * path to the legal documents 1132 * @param tagoption name of the files to use 1133 * for the legal documents and the Etag 1134 * @return NULL on error 1135 */ 1136 struct TALER_MHD_Legal * 1137 TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg, 1138 const char *section, 1139 const char *diroption, 1140 const char *tagoption); 1141 1142 1143 /** 1144 * Free set of legal documents 1145 * 1146 * @param legal legal documents to free 1147 */ 1148 void 1149 TALER_MHD_legal_free (struct TALER_MHD_Legal *legal); 1150 1151 1152 /** 1153 * Generate a response with a legal document in 1154 * the format and language of the user's choosing. 1155 * 1156 * @param conn HTTP connection to handle 1157 * @param legal legal document to serve 1158 * @return MHD result code 1159 */ 1160 enum MHD_Result 1161 TALER_MHD_reply_legal (struct MHD_Connection *conn, 1162 struct TALER_MHD_Legal *legal); 1163 1164 1165 /** 1166 * Send back a "204 No Content" response with headers 1167 * for the CORS pre-flight request. 1168 * 1169 * @param connection the MHD connection 1170 * @return MHD result code 1171 */ 1172 enum MHD_Result 1173 TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection); 1174 1175 1176 /** 1177 * Load SPA files from @a dir, relative to the project's 1178 * data directory. 1179 * 1180 * 1181 * @param pd project data to use to determine the parent directory 1182 * @param dir directory suffix to append to our data directory with the location of the files of the SPA 1183 * @return handle to serve static files from @a dir 1184 */ 1185 struct TALER_MHD_Spa * 1186 TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd, 1187 const char *dir); 1188 1189 /** 1190 * Load SPA files from absolute path to directory @a dn. 1191 * 1192 * 1193 * @param dn directory with the location of the files of the SPA, 1194 * should be an absolute path. 1195 * @return handle to serve static files from @a dir 1196 */ 1197 struct TALER_MHD_Spa * 1198 TALER_MHD_spa_load_dir (const char *dn); 1199 1200 1201 /** 1202 * Release resources used by SPA handler. 1203 * 1204 * @param[in] spa data structure to release 1205 */ 1206 void 1207 TALER_MHD_spa_free (struct TALER_MHD_Spa *spa); 1208 1209 1210 /** 1211 * Handle HTTP request for files in a @a spa. Generates 1212 * a 404 if no file at @a path does exists. 1213 * 1214 * @param spa the SPA to serve files from 1215 * @param connection HTTP connection to return data on 1216 * @param path request path to match against the @a spa 1217 * @return MHD status code to give to MHD 1218 */ 1219 enum MHD_Result 1220 TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa, 1221 struct MHD_Connection *connection, 1222 const char *path); 1223 1224 1225 /** 1226 * Information about a document #TALER_MHD_typst() should output. 1227 */ 1228 struct TALER_MHD_TypstDocument 1229 { 1230 /** 1231 * Form name, used to determine the Typst template to use. 1232 * NULL if @e data is a JSON string with a PDF to inline. 1233 */ 1234 const char *form_name; 1235 1236 /** 1237 * Typst version of the form, if NULL we will use "0.0.0". 1238 */ 1239 const char *form_version; 1240 1241 /** 1242 * Form data. 1243 */ 1244 const json_t *data; 1245 }; 1246 1247 1248 /** 1249 * Context for generating PDF responses. 1250 */ 1251 struct TALER_MHD_TypstContext; 1252 1253 1254 /** 1255 * Result from a #TALER_MHD_typst() operation. 1256 */ 1257 struct TALER_MHD_TypstResponse 1258 { 1259 1260 /** 1261 * Error status of the operation. 1262 */ 1263 enum TALER_ErrorCode ec; 1264 1265 /** 1266 * Details depending on @e ec. 1267 */ 1268 union 1269 { 1270 /** 1271 * Hint if @e ec is not #TALER_EC_NONE. 1272 */ 1273 const char *hint; 1274 1275 /** 1276 * Filename with the result if @e ec is #TALER_EC_NONE. 1277 */ 1278 const char *filename; 1279 } details; 1280 1281 }; 1282 1283 /** 1284 * Function called with the result of a #TALER_MHD_typst() operation. 1285 * 1286 * @param cls closure 1287 * @param tr result of the operation 1288 */ 1289 typedef void 1290 (*TALER_MHD_TypstResultCallback) (void *cls, 1291 const struct TALER_MHD_TypstResponse *tr); 1292 1293 1294 /** 1295 * Generate PDFs using Typst and combine them using pdftk. The 1296 * file will be returned to @a cb and after @a cb returns all data 1297 * will be deleted from the local disk. 1298 * 1299 * @param pd project data to use 1300 * @param cfg configuration to use (where to find Typst templates) 1301 * @param remove_on_exit should the directory be removed when done? 1302 * @param cfg_section_name name of the configuration section to use 1303 * @param num_documents length of the @a docs array 1304 * @param docs list of documents to combine into one large PDF 1305 * @param cb function to call with the resulting file(name) 1306 * @param cb_cls closure for @a cb 1307 * @return NULL on error 1308 */ 1309 struct TALER_MHD_TypstContext * 1310 TALER_MHD_typst ( 1311 const struct GNUNET_OS_ProjectData *pd, 1312 const struct GNUNET_CONFIGURATION_Handle *cfg, 1313 bool remove_on_exit, 1314 const char *cfg_section_name, 1315 unsigned int num_documents, 1316 const struct TALER_MHD_TypstDocument docs[static num_documents], 1317 TALER_MHD_TypstResultCallback cb, 1318 void *cb_cls); 1319 1320 1321 /** 1322 * Abort all typst response generation processes. 1323 * To be used when the system is shutting down. 1324 */ 1325 void 1326 TALER_MHD_typst_cancel (struct TALER_MHD_TypstContext *tc); 1327 1328 1329 /** 1330 * Create HTTP response from the PDF file at @a filename 1331 * 1332 * @param filename file to return as PDF 1333 * @return NULL on error 1334 */ 1335 struct MHD_Response * 1336 TALER_MHD_response_from_pdf_file (const char *filename); 1337 1338 1339 #endif