exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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