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