exchange

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

commit 42c973c010b06d630cc2e94c906998c0db46f453
parent c2b2391bec10c9fc6090da32f49d797195af1fc2
Author: Iván Ávalos <avalos@disroot.org>
Date:   Thu,  1 Oct 2026 14:33:03 +0200

remove mhd2 dependency

Diffstat:
Mmeson.build | 6------
Msrc/include/taler/meson.build | 7-------
Dsrc/include/taler/taler_mhd2_lib.h | 1072-------------------------------------------------------------------------------
Msrc/mhd/meson.build | 40----------------------------------------
Dsrc/mhd/mhd2.c | 109-------------------------------------------------------------------------------
Dsrc/mhd/mhd2_legal.c | 702-------------------------------------------------------------------------------
Dsrc/mhd/mhd2_responses.c | 462-------------------------------------------------------------------------------
Dsrc/mhd/mhd2_run.c | 414-------------------------------------------------------------------------------
Dsrc/mhd/mhd2_spa.c | 390-------------------------------------------------------------------------------
9 files changed, 0 insertions(+), 3202 deletions(-)

diff --git a/meson.build b/meson.build @@ -106,11 +106,6 @@ if not get_option('only-doc') mhd_dep = cc.find_library('microhttpd', required: true) endif - mhd2_dep = dependency('libmicrohttpd2', required: false) - if not mhd2_dep.found() - mhd_dep2 = cc.find_library('microhttpd2', required: false) - endif - json_dep = dependency('jansson', required: false) if not json_dep.found() json_dep = cc.find_library('jansson', required: true) @@ -287,7 +282,6 @@ if not get_option('only-doc') ['libtalercurl', '1:0:1'], ['libtalerpq', '1:0:0'], ['libtalermhd', '8:0:1'], - ['libtalermhd2', '0:0:0'], ['libtalertemplating', '1:1:1'], ['libtalerbank', '6:0:0'], ['libtalerkyclogic', '3:0:0'], diff --git a/src/include/taler/meson.build b/src/include/taler/meson.build @@ -28,10 +28,3 @@ foreach h : talerinclude_headers install_headers(h, install_dir: get_option('includedir') / 'taler') endforeach - -if mhd2_dep.found() - install_headers( - 'taler_mhd2_lib.h', - install_dir: get_option('includedir') / 'taler', - ) -endif diff --git a/src/include/taler/taler_mhd2_lib.h b/src/include/taler/taler_mhd2_lib.h @@ -1,1072 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file taler_mhd2_lib.h - * @brief API for generating MHD replies - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#ifndef TALER_MHD2_LIB_H -#define TALER_MHD2_LIB_H -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd2.h> -#include <taler/taler_error_codes.h> -#include <taler/taler_util.h> - - -/** - * Maximum POST request size. - */ -#define TALER_MHD2_REQUEST_BUFFER_MAX (1024 * 1024 * 16) - - -/** - * Global options for response generation. - */ -enum TALER_MHD2_GlobalOptions -{ - - /** - * Use defaults. - */ - TALER_MHD2_GO_NONE = 0, - - /** - * Add "Connection: Close" header. - */ - TALER_MHD2_GO_FORCE_CONNECTION_CLOSE = 1, - - /** - * Disable use of compression, even if the client - * supports it. - */ - TALER_MHD2_GO_DISABLE_COMPRESSION = 2 - -}; - - -/** - * Find out if an MHD connection is using HTTPS (either - * directly or via proxy). - * - * @param request MHD request - * @returns #GNUNET_YES if the MHD connection is using https, - * #GNUNET_NO if the MHD connection is using http, - * #GNUNET_SYSERR if the connection type couldn't be determined - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_is_https (struct MHD_Request *request); - - -/** - * Convert query argument to @a yna value. - * - * @param request request to take query argument from - * @param arg argument to try for - * @param default_val value to assign if the argument is not present - * @param[out] yna value to set - * @return true on success, false if the parameter was malformed - */ -bool -TALER_MHD2_arg_to_yna (struct MHD_Request *request, - const char *arg, - enum TALER_EXCHANGE_YesNoAll default_val, - enum TALER_EXCHANGE_YesNoAll *yna); - - -/** - * Set global options for response generation within libtalermhd. - * - * @param go global options to use - */ -void -TALER_MHD2_setup (enum TALER_MHD2_GlobalOptions go); - - -/** - * Add headers we want to return in every response. Useful for testing, like - * if we want to always close connections. - * - * @param response response to modify - * @param allow_store set to true to NOT add a "Cache-Control" - * directive that prevents caches and browsers from storing the data; - * if false, we set "Cache-Control: no-store" (privacy by default); - * set to true if the response contains no personal data - */ -void -TALER_MHD2_add_global_headers (struct MHD_Response *response, - bool allow_store); - - -/** - * Try to compress a response body. Updates @a buf and @a buf_size. - * - * @param[in,out] buf pointer to body to compress - * @param[in,out] buf_size pointer to initial size of @a buf - * @return true if @a buf was compressed - */ -bool -TALER_MHD2_body_compress (void **buf, - size_t *buf_size); - - -/** - * Is HTTP body deflate compression supported by the client? - * - * @param request the request to check - * @return true if 'deflate' compression is allowed - */ -bool -TALER_MHD2_can_compress (struct MHD_Request *request); - - -/** - * Send JSON object as response. - * - * @param request the MHD request to handle, used to determine - * whether compression is possible - * @param json the json object - * @param sc the http response code - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_json (struct MHD_Request *request, - const json_t *json, - enum MHD_HTTP_StatusCode sc); - - -/** - * Send JSON object as response, and free the @a json - * object. - * - * @param request the MHD request to handle - * @param json the json object (freed!) - * @param sc the http response code - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_json_steal (struct MHD_Request *request, - json_t *json, - enum MHD_HTTP_StatusCode sc); - - -/** - * Make JSON response object. - * - * @param sc HTTP response code to use - * @param json the json object - * @return MHD response object - */ -struct MHD_Response * -TALER_MHD2_make_json (enum MHD_HTTP_StatusCode sc, - const json_t *json); - - -/** - * Make JSON response object and free @a json. - * - * @param sc HTTP response code to use - * @param json the json object, freed. - * @return MHD response object - */ -struct MHD_Response * -TALER_MHD2_make_json_steal (enum MHD_HTTP_StatusCode sc, - json_t *json); - - -/** - * Make response object - * - * @param sc HTTP status code to use - * @param ... varargs - * @return MHD action - */ -#define TALER_MHD2_MAKE_JSON_PACK(sc,...) \ - TALER_MHD2_make_json_steal (sc, GNUNET_JSON_PACK (__VA_ARGS__)) - - -/** - * Pack Taler error code @a ec and associated hint into a - * JSON object. - * - * @param ec error code to pack - * @return packer array entries (two!) - */ -#define TALER_MHD2_PACK_EC(ec) \ - GNUNET_JSON_pack_uint64 ("code", ec), \ - GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec)) - -/** - * Create a response indicating an internal error. - * - * @param ec error code to return - * @param detail additional optional detail about the error, can be NULL - * @return a MHD response object - */ -struct MHD_Response * -TALER_MHD2_make_error (enum TALER_ErrorCode ec, - const char *detail); - - -/** - * Function to call to handle the request by building a JSON - * reply from varargs. - * - * @param request the MHD request to handle - * @param sc HTTP response code to use - * @param ... varargs of JSON pack specification - * @return MHD action - */ -#define TALER_MHD2_REPLY_JSON_PACK(request,sc,...) \ - TALER_MHD2_reply_json_steal (request, GNUNET_JSON_PACK (__VA_ARGS__), \ - sc) - - -/** - * Send a response indicating an error. - * - * @param request the MHD request to use - * @param sc HTTP status code to use - * @param ec error code uniquely identifying the error - * @param detail additional optional detail about the error - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_with_error (struct MHD_Request *request, - enum MHD_HTTP_StatusCode sc, - enum TALER_ErrorCode ec, - const char *detail); - - -/** - * Send a response indicating an error. The HTTP status code is - * to be derived from the @a ec. - * - * @param request the MHD request to use - * @param ec error code uniquely identifying the error - * @param detail additional optional detail about the error - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_with_ec (struct MHD_Request *request, - enum TALER_ErrorCode ec, - const char *detail); - - -/** - * Produce HTTP "Date:" header. - * - * @param at time to write to @a date - * @param[out] date where to write the header, with - * at least 128 bytes available space. - */ -void -TALER_MHD2_get_date_string (struct GNUNET_TIME_Absolute at, - char date[128]); - - -/** - * Send a response indicating that the request was too big. - * - * @param request the MHD request to use - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_request_too_large (struct MHD_Request *request); - - -/** - * Function to call to handle the request by sending - * back a redirect to the AGPL source code. - * - * @param request the MHD request to handle - * @param url where to redirect for the sources - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_agpl (struct MHD_Request *request, - const char *url); - - -/** - * Function to call to handle the request by sending - * back static data. - * - * @param request the MHD request to handle - * @param sc status code to return - * @param mime_type content-type to use - * @param body response payload - * @param body_size number of bytes in @a body - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_static (struct MHD_Request *request, - enum MHD_HTTP_StatusCode sc, - const char *mime_type, - const char *body, - size_t body_size); - - -/* The request-parsing surface below has not been ported to MHD2 yet: - none of the functions it declares is implemented anywhere, so the - block must stay disabled until src/mhd/mhd2_parsing.c exists. Note - that "#if 0" is deliberate: a guard on an undefined macro name would - silently flip meaning as soon as anything defines that name, handing - the user a header that compiles and then fails to link. */ -#if 0 - -/** - * Process a POST request containing a JSON object. This - * function realizes an MHD POST processor that will - * (incrementally) process JSON data uploaded to the HTTP - * server. It will store the required state in the - * "request_cls", which must be cleaned up using - * #TALER_MHD2_parse_post_cleanup_callback(). - * - * @param request the MHD request - * @param con_cls the closure (points to a `struct Buffer *`) - * @param upload_data the POST data - * @param upload_data_size number of bytes in @a upload_data - * @param json the JSON object for a completed request - * @return - * #GNUNET_YES if json object was parsed or at least - * may be parsed in the future (call again); - * `*json` will be NULL if we need to be called again, - * and non-NULL if we are done. - * #GNUNET_NO is request incomplete or invalid - * (error message was generated) - * #GNUNET_SYSERR on internal error - * (we could not even queue an error message, - * close HTTP session with MHD_NO) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_post_json (struct MHD_Request *request, - void **con_cls, - const char *upload_data, - size_t *upload_data_size, - json_t **json); - - -/** - * Function called whenever we are done with a request - * to clean up our state. - * - * @param con_cls value as it was left by - * #TALER_MHD2_parse_post_json(), to be cleaned up - */ -void -TALER_MHD2_parse_post_cleanup_callback (void *con_cls); - - -/** - * Parse JSON object into components based on the given field - * specification. If parsing fails, we return an HTTP - * status code of 400 (#MHD_HTTP_BAD_REQUEST). - * - * @param request the request to send an error response to - * @param root the JSON node to start the navigation at. - * @param spec field specification for the parser - * @return - * #GNUNET_YES if navigation was successful (caller is responsible - * for freeing allocated variable-size data using - * GNUNET_JSON_parse_free() when done) - * #GNUNET_NO if json is malformed, error response was generated - * #GNUNET_SYSERR on internal error - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_json_data (struct MHD_Request *request, - const json_t *root, - struct GNUNET_JSON_Specification *spec); - - -/** - * Parse JSON object that we (the server!) generated into components based on - * the given field specification. The difference to - * #TALER_MHD2_parse_json_data() is that this function will fail - * with an HTTP failure of 500 (internal server error) in case - * parsing fails, instead of blaming it on the client with a - * 400 (#MHD_HTTP_BAD_REQUEST). - * - * @param request the request to send an error response to - * @param root the JSON node to start the navigation at. - * @param spec field specification for the parser - * @return - * #GNUNET_YES if navigation was successful (caller is responsible - * for freeing allocated variable-size data using - * GNUNET_JSON_parse_free() when done) - * #GNUNET_NO if json is malformed, error response was generated - * #GNUNET_SYSERR on internal error - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_internal_json_data (struct MHD_Request *request, - const json_t *root, - struct GNUNET_JSON_Specification *spec); - - -/** - * Parse JSON array into components based on the given field - * specification. Generates error response on parse errors. - * - * @param request the request to send an error response to - * @param root the JSON node to start the navigation at. - * @param[in,out] spec field specification for the parser - * @param ... -1-terminated list of array offsets of type 'int' - * @return - * #GNUNET_YES if navigation was successful (caller is responsible - * for freeing allocated variable-size data using - * GNUNET_JSON_parse_free() when done) - * #GNUNET_NO if json is malformed, error response was generated - * #GNUNET_SYSERR on internal error - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_json_array (struct MHD_Request *request, - const json_t *root, - struct GNUNET_JSON_Specification *spec, - ...); - - -/** - * Extract optional "timeout_ms" argument from request. - * - * @param request the MHD request - * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, - * the current time plus the value given under "timeout_ms" otherwise - * @return #GNUNET_OK on success, #GNUNET_NO if an - * error was returned on @a request (caller should return #MHD_YES) and - * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_request_arg_timeout (struct MHD_Request *request, - struct GNUNET_TIME_Absolute *expiration); - - -/** - * Extract optional "timeout_ms" argument from request. - * Macro that *returns* #MHD_YES/#MHD_NO if the "timeout_ms" - * argument existed but failed to parse. - * - * @param request the MHD request - * @param[out] expiration set to #GNUNET_TIME_UNIT_ZERO_ABS if there was no timeout, - * the current time plus the value given under "timeout_ms" otherwise - */ -#define TALER_MHD2_parse_request_timeout(request,expiration) \ - do { \ - switch (TALER_MHD2_parse_request_arg_timeout (request, \ - expiration)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - case GNUNET_OK: \ - break; \ - } \ - } while (0) - - -/** - * Extract optional timestamp argument from request. - * - * @param request the MHD request - * @param fname name of the argument to parse - * @param[out] ts set to #GNUNET_TIME_UNIT_ZERO_TS if there was no timestamp - * @return #GNUNET_OK on success, #GNUNET_NO if an - * error was returned on @a request (caller should return #MHD_YES) and - * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_request_arg_timestamp (struct MHD_Request *request, - const char *fname, - struct GNUNET_TIME_Timestamp *ts); - - -/** - * Extract optional timestamp argument from request. - * Macro that *returns* #MHD_YES/#MHD_NO if the timestamp - * argument existed but failed to parse. - * - * @param request the MHD request - * @param fname name of the argument - * @param[out] ts set to #GNUNET_TIME_UNIT_ZERO_TS if there was no timestamp - */ -#define TALER_MHD2_parse_request_timestamp(request,fname,ts) \ - do { \ - switch (TALER_MHD2_parse_request_arg_timestamp (request, \ - fname, \ - ts)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - case GNUNET_OK: \ - break; \ - } \ - } while (0) - - -/** - * Extract optional "yes/no/all" argument from request. - * Macro that *returns* #MHD_YES/#MHD_NO if the - * argument existed but failed to parse. - * - * @param request the MHD request - * @param name name of the query parameter to parse - * @param def default value to set if absent - * @param[out] ret set to the yes/no/all value - */ -#define TALER_MHD2_parse_request_yna(request,name,def,ret) \ - do { \ - if (! (TALER_arg_to_yna (request, \ - name, \ - def, \ - ret)) ) \ - { \ - GNUNET_break_op (0); \ - return TALER_MHD2_reply_with_error ( \ - request, \ - MHD_HTTP_BAD_REQUEST, \ - TALER_EC_GENERIC_PARAMETER_MALFORMED, \ - name); \ - } \ - } while (0) - -/** - * Extract optional numeric limit argument from request. - * - * @param request the MHD request - * @param name name of the query parameter - * @param[out] off set to the offset, unchanged if the - * option was not given - * @return #GNUNET_OK on success, - * #GNUNET_NO if an error was returned on @a request (caller should return #MHD_YES) and - * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_request_arg_number (struct MHD_Request *request, - const char *name, - uint64_t *off); - - -/** - * Extract optional numeric argument from request. - * Macro that *returns* #MHD_YES/#MHD_NO if the - * requested argument existed but failed to parse. - * - * @param request the MHD request - * @param name name of the argument to parse - * @param[out] off set to the given numeric value, - * unchanged if value was not specified - */ -#define TALER_MHD2_parse_request_number(request,name,off) \ - do { \ - switch (TALER_MHD2_parse_request_arg_number (request, \ - name, \ - off)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - case GNUNET_OK: \ - break; \ - } \ - } while (0) - - -/** - * Extract optional signed numeric limit argument from request. - * - * @param request the MHD request - * @param name name of the query parameter - * @param[out] val set to the signed value, unchanged if the - * option was not given - * @return #GNUNET_OK on success, - * #GNUNET_NO if an error was returned on @a request (caller should return #MHD_YES) and - * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_request_arg_snumber (struct MHD_Request *request, - const char *name, - int64_t *val); - - -/** - * Extract optional numeric argument from request. - * Macro that *returns* #MHD_YES/#MHD_NO if the - * requested argument existed but failed to parse. - * - * @param request the MHD request - * @param name name of the argument to parse - * @param[out] val set to the given numeric value, - * unchanged if value was not specified - */ -#define TALER_MHD2_parse_request_snumber(request,name,val) \ - do { \ - switch (TALER_MHD2_parse_request_arg_snumber (request, \ - name, \ - val)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - case GNUNET_OK: \ - break; \ - } \ - } while (0) - - -/** - * Extract optional amount argument from request. - * - * @param request the MHD request - * @param name name of the query parameter - * @param[out] val set to the amount, unchanged if the - * option was not given - * @return #GNUNET_OK on success, - * #GNUNET_NO if an error was returned on @a request (caller should return #MHD_YES) and - * #GNUNET_SYSERR if we failed to return an error (caller should return #MHD_NO) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_request_arg_amount (struct MHD_Request *request, - const char *name, - struct TALER_Amount *val); - - -/** - * Extract optional amount argument from request. Macro that *returns* - * #MHD_YES/#MHD_NO if the requested argument existed but failed to parse. - * - * @param request the MHD request - * @param name name of the argument to parse - * @param[out] val set to the given amount, - * unchanged if value was not specified - */ -#define TALER_MHD2_parse_request_amount(request,name,val) \ - do { \ - switch (TALER_MHD2_parse_request_arg_amount (request, \ - name, \ - val)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - case GNUNET_OK: \ - break; \ - } \ - } while (0) - - -/** - * Extract fixed-size base32crockford encoded data from request argument. - * - * Queues an error response to the request if the parameter is missing or - * invalid. - * - * @param request the MHD request - * @param param_name the name of the parameter with the key - * @param[out] out_data pointer to store the result - * @param out_size expected size of @a out_data - * @param[out] present set to true if argument was found - * @return - * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_request_arg_data (struct MHD_Request *request, - const char *param_name, - void *out_data, - size_t out_size, - bool *present); - - -/** - * Extract fixed-size base32crockford encoded data from request header. - * - * Queues an error response to the request if the parameter is missing or - * invalid. - * - * @param request the MHD request - * @param header_name the name of the HTTP header with the value - * @param[out] out_data pointer to store the result - * @param out_size expected size of @a out_data - * @param[out] present set to true if argument was found - * @return - * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_parse_request_header_data (struct MHD_Request *request, - const char *header_name, - void *out_data, - size_t out_size, - bool *present); - -/** - * Extract fixed-size base32crockford encoded data from request. - * - * @param request the MHD request - * @param name the name of the parameter with the key - * @param[out] val pointer to store the result, type must determine size - * @param[in,out] required pass true to require presence of this argument; if 'false' - * set to true if the argument was found - * @return - * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is absent or malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -#define TALER_MHD2_parse_request_arg_auto(request,name,val,required) \ - do { \ - bool p; \ - switch (TALER_MHD2_parse_request_arg_data (request, name, \ - val, sizeof (*val), &p)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - return MHD_YES; \ - case GNUNET_OK: \ - if (required & (! p)) \ - { \ - GNUNET_break_op (0); \ - return TALER_MHD2_reply_with_error ( \ - request, \ - MHD_HTTP_BAD_REQUEST, \ - TALER_EC_GENERIC_PARAMETER_MISSING, \ - name); \ - } \ - required = p; \ - break; \ - } \ - } while (0) - - -/** - * Extract required fixed-size base32crockford encoded data from request. - * - * @param request the MHD request - * @param name the name of the parameter with the key - * @param[out] val pointer to store the result, type must determine size - * @return - * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is absent or malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -#define TALER_MHD2_parse_request_arg_auto_t(request,name,val) \ - do { \ - bool b = true; \ - TALER_MHD2_parse_request_arg_auto (request,name,val,b); \ - } while (0) - -/** - * Extract fixed-size base32crockford encoded data from request. - * - * @param request the MHD request - * @param name the name of the header with the key - * @param[out] val pointer to store the result, type must determine size - * @param[in,out] required pass true to require presence of this argument; if 'false' - * set to true if the argument was found - * @return - * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is absent or malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -#define TALER_MHD2_parse_request_header_auto(request,name,val,required) \ - do { \ - bool p; \ - switch (TALER_MHD2_parse_request_header_data (request, name, \ - val, sizeof (*val), &p)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - return MHD_YES; \ - case GNUNET_OK: \ - if (required & (! p)) \ - return TALER_MHD2_reply_with_error ( \ - request, \ - MHD_HTTP_BAD_REQUEST, \ - TALER_EC_GENERIC_PARAMETER_MISSING, \ - name); \ - required = p; \ - break; \ - } \ - } while (0) - - -/** - * Extract required fixed-size base32crockford encoded data from request. - * - * @param request the MHD request - * @param name the name of the header with the key - * @param[out] val pointer to store the result, type must determine size - * @return - * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is absent or malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -#define TALER_MHD2_parse_request_header_auto_t(request,name,val) \ - do { \ - bool b = true; \ - TALER_MHD2_parse_request_header_auto (request,name,val,b); \ - } while (0) - - -/** - * Check that the 'Content-Length' header is giving - * a length below @a max_len. If not, return an - * appropriate error response and return the - * correct #MHD_YES/#MHD_NO value from this function. - * - * @param request the MHD request - * @param max_len maximum allowed content length - * @return - * #GNUNET_YES if the the argument is present - * #GNUNET_NO if the argument is absent or malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -enum GNUNET_GenericReturnValue -TALER_MHD2_check_content_length_ (struct MHD_Request *request, - unsigned long long max_len); - - -/** - * Check that the 'Content-Length' header is giving - * a length below @a max_len. If not, return an - * appropriate error response and return the - * correct #MHD_YES/#MHD_NO value from this function. - * - * @param request the MHD request - * @param max_len maximum allowed content length - */ -#define TALER_MHD2_check_content_length(request,max_len) \ - do { \ - switch (TALER_MHD2_check_content_length_ (request, max_len)) \ - { \ - case GNUNET_SYSERR: \ - GNUNET_break (0); \ - return MHD_NO; \ - case GNUNET_NO: \ - GNUNET_break_op (0); \ - return MHD_YES; \ - case GNUNET_OK: \ - break; \ - } \ - } while (0) - -#endif - - -/** - * Function called for logging by MHD. - * - * @param cls closure, NULL - * @param fm format string (`printf()`-style) - * @param ap arguments to @a fm - */ -void -TALER_MHD_handle_logs (void *cls, - const char *fm, - va_list ap); - - -/** - * Function called on each successfully bound listen - * socket by #TALER_MHD_listen_bind(). - * - * @param cls closure - * @param fd bound listen socket (must be used and eventually - * closed by the callee). Never -1. - */ -typedef void -(*TALER_MHD_ListenSocketCallback)(void *cls, - int fd); - - -/** - * Bind a listen socket to the UNIX domain path, - * or the TCP port(s) and IP address(es) configured, - * or return to @a cb the inherited sockets from systemd, - * all depending on what was specified in @a cfg in - * the section named @a section. - * - * @param cfg configuration to parse - * @param section configuration section to use - * @param cb function to call with each bound socket - * @param cb_cls closure for @a cb - * @return #GNUNET_OK on success (all configured sockets were bound) - * #GNUNET_NO if some configured binding failed but the config is OK, - * note that some listen sockets may have been created - * #GNUNET_SYSERR if the configuration is invalid - */ -enum GNUNET_GenericReturnValue -TALER_MHD_listen_bind (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - TALER_MHD_ListenSocketCallback cb, - void *cb_cls); - - -/** - * Start to run an event loop for @a daemon. - * Only one daemon can be running per process - * using this API. - * - * @param daemon the MHD service to run - */ -void -TALER_MHD2_daemon_start (struct MHD_Daemon *daemon); - - -/** - * Stop running the event loops of all MHD daemons. - */ -void -TALER_MHD2_daemons_halt (void); - - -/** - * Stop accepting new connections on all MHD daemons - * (and close the listen sockets). - */ -void -TALER_MHD2_daemons_quiesce (void); - -/** - * Wake up all daemons. - */ -void -TALER_MHD2_daemons_trigger (void); - -/** - * Destroy all state associated with all MHD daemons. - */ -void -TALER_MHD2_daemons_destroy (void); - - -/** - * Prepared responses for legal documents - * (terms of service, privacy policy). - */ -struct TALER_MHD2_Legal; - - -/** - * Load set of legal documents as specified in @a cfg in section @a section - * where the Etag is given under the @a tagoption and the directory under - * the @a diroption. - * - * @param cfg configuration to use - * @param section section to load values from - * @param diroption name of the option with the - * path to the legal documents - * @param tagoption name of the files to use - * for the legal documents and the Etag - * @return NULL on error - */ -struct TALER_MHD2_Legal * -TALER_MHD2_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - const char *diroption, - const char *tagoption); - - -/** - * Free set of legal documents - * - * @param legal legal documents to free - */ -void -TALER_MHD2_legal_free (struct TALER_MHD2_Legal *legal); - - -/** - * Generate a response with a legal document in - * the format and language of the user's choosing. - * - * @param conn HTTP request to handle - * @param legal legal document to serve - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_legal (struct MHD_Request *conn, - struct TALER_MHD2_Legal *legal); - - -/** - * Send back a "204 No Content" response with headers - * for the CORS pre-flight request. - * - * @param request the MHD request - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_reply_cors_preflight (struct MHD_Request *request); - - -/** - * Load SPA files from @a dir - * - * @param pd project data to use to determine the parent directory - * @param dir directory suffix to append to our data directory with the location of the files of the SPA - * @return handle to serve static files from @a dir - */ -struct TALER_MHD2_Spa * -TALER_MHD2_spa_load (const struct GNUNET_OS_ProjectData *pd, - const char *dir); - - -/** - * Release resources used by SPA handler. - * - * @param[in] spa data structure to release - */ -void -TALER_MHD2_spa_free (struct TALER_MHD2_Spa *spa); - - -/** - * Handle HTTP request for files in a @a spa. Generates - * a 404 if no file at @a path does exists. - * - * @param spa the SPA to serve files from - * @param request HTTP request to return data on - * @param path request path to match against the @a spa - * @return MHD action - */ -const struct MHD_Action * -TALER_MHD2_spa_handler (const struct TALER_MHD2_Spa *spa, - struct MHD_Request *request, - const char *path); - - -#endif diff --git a/src/mhd/meson.build b/src/mhd/meson.build @@ -83,43 +83,3 @@ foreach t : talermhd_tests ) endforeach -if mhd2_dep.found() - - libtalermhd2_src = [ - 'mhd2.c', - 'mhd_config.c', - 'mhd2_legal.c', - 'mhd2_responses.c', - 'mhd2_run.c', - 'mhd2_spa.c', - ] - - libtalermhd2 = library( - 'talermhd2', - libtalermhd2_src, - soversion: solibversions['libtalermhd2']['soversion'], - version: solibversions['libtalermhd2']['soversion'], - install_rpath: rpath_option, - dependencies: [ - libtalerutil_dep, - libtalerjson_dep, - gnunetutil_dep, - gnunetjson_dep, - gnunetmhd_dep, - json_dep, - mhd2_dep, - zlib_dep, - ], - include_directories: [incdir, configuration_inc], - install: true, - install_dir: get_option('libdir'), - ) - - libtalermhd2_dep = declare_dependency(link_with: libtalermhd2) - pkg.generate( - libtalermhd2, - url: 'https://taler.net', - description: 'GNU Taler microhttpd2 wrapper library', - ) - -endif diff --git a/src/mhd/mhd2.c b/src/mhd/mhd2.c @@ -1,109 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file mhd2.c - * @brief MHD utility functions (used by the merchant backend) - * @author Christian Grothoff - */ -#include "taler/taler_util.h" -#include "taler/taler_mhd2_lib.h" - - -enum GNUNET_GenericReturnValue -TALER_MHD2_is_https (struct MHD_Request *request) -{ - struct MHD_StringNullable forwarded_proto; - union MHD_RequestInfoFixedData ci; - union MHD_DaemonInfoFixedData di; - - if ( (MHD_NO != - MHD_request_get_value (request, - MHD_VK_HEADER, - "X-Forwarded-Proto", - &forwarded_proto)) && - (NULL != forwarded_proto.cstr) ) - { - if (0 == strcasecmp (forwarded_proto.cstr, - "https")) - return GNUNET_YES; - if (0 == strcasecmp (forwarded_proto.cstr, - "http")) - return GNUNET_NO; - GNUNET_break (0); - return GNUNET_SYSERR; - } - /* likely not reverse proxy, figure out if we are - http by asking MHD */ - if (MHD_SC_OK != - MHD_request_get_info_fixed (request, - MHD_REQUEST_INFO_FIXED_DAEMON, - &ci)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (MHD_SC_OK != - MHD_daemon_get_info_fixed (ci.v_daemon, - MHD_DAEMON_INFO_FIXED_TLS_BACKEND, - &di)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - if (MHD_TLS_BACKEND_NONE != di.v_tls_backend) - return GNUNET_YES; - return GNUNET_NO; -} - - -bool -TALER_MHD2_arg_to_yna (struct MHD_Request *request, - const char *arg, - enum TALER_EXCHANGE_YesNoAll default_val, - enum TALER_EXCHANGE_YesNoAll *yna) -{ - struct MHD_StringNullable nstr; - - if ( (MHD_NO == - MHD_request_get_value (request, - MHD_VK_URI_QUERY_PARAM, - arg, - &nstr)) || - (NULL == nstr.cstr) ) - { - *yna = default_val; - return true; - } - if (0 == strcasecmp (nstr.cstr, - "yes")) - { - *yna = TALER_EXCHANGE_YNA_YES; - return true; - } - if (0 == strcasecmp (nstr.cstr, - "no")) - { - *yna = TALER_EXCHANGE_YNA_NO; - return true; - } - if (0 == strcasecmp (nstr.cstr, - "all")) - { - *yna = TALER_EXCHANGE_YNA_ALL; - return true; - } - return false; -} diff --git a/src/mhd/mhd2_legal.c b/src/mhd/mhd2_legal.c @@ -1,702 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2019--2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file mhd2_legal.c - * @brief API for returning legal documents based on client language - * and content type preferences - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd2.h> -#include "taler/taler_util.h" -#include "taler/taler_mhd2_lib.h" - -/** - * How long should browsers/proxies cache the "legal" replies? - */ -#define MAX_TERMS_CACHING GNUNET_TIME_UNIT_DAYS - -/** - * HTTP header with the version of the terms of service. - */ -#define TALER_TERMS_VERSION "Taler-Terms-Version" - -/** - * Entry in the terms-of-service array. - */ -struct Terms -{ - /** - * Kept in a DLL. - */ - struct Terms *prev; - - /** - * Kept in a DLL. - */ - struct Terms *next; - - /** - * Mime type of the terms. - */ - const char *mime_type; - - /** - * The terms (NOT 0-terminated!), mmap()'ed. Do not free, - * use munmap() instead. - */ - void *terms; - - /** - * The desired language. - */ - char *language; - - /** - * deflated @e terms, to return if client supports deflate compression. - * malloc()'ed. NULL if @e terms does not compress. - */ - void *compressed_terms; - - /** - * Etag we use for this response. - */ - char *terms_etag; - - /** - * Number of bytes in @e terms. - */ - size_t terms_size; - - /** - * Number of bytes in @e compressed_terms. - */ - size_t compressed_terms_size; - - /** - * Sorting key by format preference in case - * everything else is equal. Higher is preferred. - */ - unsigned int priority; - -}; - - -/** - * Prepared responses for legal documents - * (terms of service, privacy policy). - */ -struct TALER_MHD2_Legal -{ - /** - * DLL of terms of service. - */ - struct Terms *terms_head; - - /** - * DLL of terms of service. - */ - struct Terms *terms_tail; - - /** - * Etag to use for the terms of service (= version). - */ - char *terms_version; -}; - - -const struct MHD_Action * -TALER_MHD2_reply_legal (struct MHD_Request *request, - struct TALER_MHD2_Legal *legal) -{ - /* Default terms of service if none are configured */ - static struct Terms none = { - .mime_type = "text/plain", - .terms = (void *) "not configured", - .language = (void *) "en", - .terms_size = strlen ("not configured") - }; - struct MHD_Response *resp; - struct Terms *t; - struct GNUNET_TIME_Absolute a; - struct GNUNET_TIME_Timestamp m; - char dat[128]; - char *langs; - - t = NULL; - langs = NULL; - a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING); - m = GNUNET_TIME_absolute_to_timestamp (a); - /* Round up to next full day to ensure the expiration - time does not become a fingerprint! */ - a = GNUNET_TIME_absolute_round_down (a, - MAX_TERMS_CACHING); - a = GNUNET_TIME_absolute_add (a, - MAX_TERMS_CACHING); - TALER_MHD2_get_date_string (m.abs_time, - dat); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Setting '%s' header to '%s'\n", - MHD_HTTP_HEADER_EXPIRES, - dat); - if (NULL == legal) - { - t = &none; - goto return_t; - } - - if (NULL != legal) - { - struct MHD_StringNullable mimen; - struct MHD_StringNullable langn; - const char *mime; - const char *lang; - double best_mime_q = 0.0; - double best_lang_q = 0.0; - - if ( (MHD_NO == - MHD_request_get_value (request, - MHD_VK_HEADER, - MHD_HTTP_HEADER_ACCEPT, - &mimen)) || - (NULL == mimen.cstr) ) - mime = "text/plain"; - else - mime = mimen.cstr; - if ( (MHD_NO == - MHD_request_get_value (request, - MHD_VK_HEADER, - MHD_HTTP_HEADER_ACCEPT_LANGUAGE, - &langn)) || - (NULL == langn.cstr) ) - lang = "en"; - else - lang = langn.cstr; - /* Find best match: must match mime type (if possible), and if - mime type matches, ideally also language */ - for (struct Terms *p = legal->terms_head; - NULL != p; - p = p->next) - { - double q; - - q = TALER_pattern_matches (mime, - p->mime_type); - if (q > best_mime_q) - best_mime_q = q; - } - for (struct Terms *p = legal->terms_head; - NULL != p; - p = p->next) - { - double q; - - q = TALER_pattern_matches (mime, - p->mime_type); - if (q < best_mime_q) - continue; - if (NULL == langs) - { - langs = GNUNET_strdup (p->language); - } - else if (NULL == strstr (langs, - p->language)) - { - char *tmp = langs; - - GNUNET_asprintf (&langs, - "%s,%s", - tmp, - p->language); - GNUNET_free (tmp); - } - q = TALER_pattern_matches (lang, - p->language); - if (NULL != t) - { - if (q < best_lang_q) - continue; - /* Client expressed no preference between these two formats, - so fall back to our own ranking instead of letting the - last entry in the list win. */ - if ( (q <= best_lang_q) && - (p->priority <= t->priority) ) - continue; - } - best_lang_q = q; - t = p; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Best match for %s/%s: %s / %s\n", - lang, - mime, - (NULL != t) ? t->mime_type : "<none>", - (NULL != t) ? t->language : "<none>"); - } - - if (NULL != t) - { - struct MHD_StringNullable etag; - - if ( (MHD_NO != - MHD_request_get_value (request, - MHD_VK_HEADER, - MHD_HTTP_HEADER_IF_NONE_MATCH, - &etag)) && - (NULL != etag.cstr) && - (NULL != t->terms_etag) && - (0 == strcasecmp (etag.cstr, - t->terms_etag)) ) - { - resp = MHD_response_from_empty (MHD_HTTP_STATUS_NOT_MODIFIED); - GNUNET_break (MHD_SC_OK == - MHD_RESPONSE_SET_OPTIONS ( - resp, - MHD_R_OPTION_HEAD_ONLY_RESPONSE (true))); - TALER_MHD2_add_global_headers (resp, - true); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_EXPIRES, - dat)); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_ETAG, - t->terms_etag)); - if (NULL != legal) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - TALER_TERMS_VERSION, - legal->terms_version)); - return MHD_action_from_response (request, - resp); - } - } - - if (NULL == t) - t = &none; /* 501 if not configured */ - -return_t: - /* try to compress the response */ - resp = NULL; - if ( (MHD_YES == - TALER_MHD2_can_compress (request)) && - (NULL != t->compressed_terms) ) - { - resp = MHD_response_from_buffer_static (t == &none - ? MHD_HTTP_STATUS_NOT_IMPLEMENTED - : MHD_HTTP_STATUS_OK, - t->compressed_terms_size, - t->compressed_terms); - if (MHD_SC_OK != - MHD_response_add_header (resp, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) - { - GNUNET_break (0); - MHD_response_destroy (resp); - resp = NULL; - } - } - if (NULL == resp) - { - /* could not generate compressed response, return uncompressed */ - resp = MHD_response_from_buffer_static (t == &none - ? MHD_HTTP_STATUS_NOT_IMPLEMENTED - : MHD_HTTP_STATUS_OK, - t->terms_size, - (void *) t->terms); - } - TALER_MHD2_add_global_headers (resp, - true); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_EXPIRES, - dat)); - if (NULL != langs) - { - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - "Avail-Languages", - langs)); - GNUNET_free (langs); - } - /* Set cache control headers: our response varies depending on these headers */ - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_VARY, - MHD_HTTP_HEADER_ACCEPT_LANGUAGE "," - MHD_HTTP_HEADER_ACCEPT "," - MHD_HTTP_HEADER_ACCEPT_ENCODING)); - /* Information is always public, revalidate after 10 days */ - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_CACHE_CONTROL, - "public,max-age=864000")); - if (NULL != t->terms_etag) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_ETAG, - t->terms_etag)); - if (NULL != legal) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - TALER_TERMS_VERSION, - legal->terms_version)); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_CONTENT_TYPE, - t->mime_type)); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (resp, - MHD_HTTP_HEADER_CONTENT_LANGUAGE, - t->language)); - return MHD_action_from_response (request, - resp); -} - - -/** - * Load all the terms of service from @a path under language @a lang - * from file @a name - * - * @param[in,out] legal where to write the result - * @param path where the terms are found - * @param lang which language directory to crawl - * @param name specific file to access - */ -static void -load_terms (struct TALER_MHD2_Legal *legal, - const char *path, - const char *lang, - const char *name) -{ - static struct MimeMap - { - const char *ext; - const char *mime; - unsigned int priority; - } mm[] = { - { .ext = ".txt", .mime = "text/plain", .priority = 150 }, - { .ext = ".html", .mime = "text/html", .priority = 100 }, - { .ext = ".htm", .mime = "text/html", .priority = 99 }, - { .ext = ".md", .mime = "text/markdown", .priority = 50 }, - { .ext = ".pdf", .mime = "application/pdf", .priority = 25 }, - { .ext = ".jpg", .mime = "image/jpeg" }, - { .ext = ".jpeg", .mime = "image/jpeg" }, - { .ext = ".png", .mime = "image/png" }, - { .ext = ".gif", .mime = "image/gif" }, - { .ext = ".epub", .mime = "application/epub+zip", .priority = 10 }, - { .ext = ".xml", .mime = "text/xml", .priority = 10 }, - { .ext = NULL, .mime = NULL } - }; - const char *ext = strrchr (name, '.'); - const char *mime; - unsigned int priority; - - if (NULL == ext) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unsupported file `%s' in directory `%s/%s': lacks extension\n", - name, - path, - lang); - return; - } - if ( (NULL == legal->terms_version) || - (0 != strncmp (legal->terms_version, - name, - ext - name)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Filename `%s' does not match Etag `%s' in directory `%s/%s'. Ignoring it.\n", - name, - legal->terms_version, - path, - lang); - return; - } - mime = NULL; - for (unsigned int i = 0; NULL != mm[i].ext; i++) - if (0 == strcasecmp (mm[i].ext, - ext)) - { - mime = mm[i].mime; - priority = mm[i].priority; - break; - } - if (NULL == mime) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Unsupported file extension `%s' of file `%s' in directory `%s/%s'\n", - ext, - name, - path, - lang); - return; - } - /* try to read the file with the terms of service */ - { - struct stat st; - char *fn; - int fd; - - GNUNET_asprintf (&fn, - "%s/%s/%s", - path, - lang, - name); - fd = open (fn, O_RDONLY); - if (-1 == fd) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "open", - fn); - GNUNET_free (fn); - return; - } - if (0 != fstat (fd, &st)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "fstat", - fn); - GNUNET_break (0 == close (fd)); - GNUNET_free (fn); - return; - } - if (SIZE_MAX < ((unsigned long long) st.st_size)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "fstat-size", - fn); - GNUNET_break (0 == close (fd)); - GNUNET_free (fn); - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Loading legal information from file `%s'\n", - fn); - { - void *buf; - size_t bsize; - - bsize = (size_t) st.st_size; - buf = mmap (NULL, - bsize, - PROT_READ, - MAP_SHARED, - fd, - 0); - if (MAP_FAILED == buf) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING, - "mmap", - fn); - GNUNET_break (0 == close (fd)); - GNUNET_free (fn); - return; - } - GNUNET_break (0 == close (fd)); - GNUNET_free (fn); - - /* insert into global list of terms of service */ - { - struct Terms *t; - struct GNUNET_HashCode hc; - - GNUNET_CRYPTO_hash (buf, - bsize, - &hc); - t = GNUNET_new (struct Terms); - t->mime_type = mime; - t->terms = buf; - t->language = GNUNET_strdup (lang); - t->terms_size = bsize; - t->priority = priority; - t->terms_etag - = GNUNET_STRINGS_data_to_string_alloc (&hc, - sizeof (hc) / 2); - buf = GNUNET_memdup (t->terms, - t->terms_size); - if (TALER_MHD2_body_compress (&buf, - &bsize)) - { - t->compressed_terms = buf; - t->compressed_terms_size = bsize; - } - else - { - GNUNET_free (buf); - } - { - struct Terms *prev = NULL; - - for (struct Terms *pos = legal->terms_head; - NULL != pos; - pos = pos->next) - { - if (pos->priority < priority) - break; - prev = pos; - } - GNUNET_CONTAINER_DLL_insert_after (legal->terms_head, - legal->terms_tail, - prev, - t); - } - } - } - } -} - - -/** - * Load all the terms of service from @a path under language @a lang. - * - * @param[in,out] legal where to write the result - * @param path where the terms are found - * @param lang which language directory to crawl - */ -static void -load_language (struct TALER_MHD2_Legal *legal, - const char *path, - const char *lang) -{ - char *dname; - DIR *d; - - GNUNET_asprintf (&dname, - "%s/%s", - path, - lang); - d = opendir (dname); - if (NULL == d) - { - GNUNET_free (dname); - return; - } - for (struct dirent *de = readdir (d); - NULL != de; - de = readdir (d)) - { - const char *fn = de->d_name; - - if (fn[0] == '.') - continue; - load_terms (legal, - path, - lang, - fn); - } - GNUNET_break (0 == closedir (d)); - GNUNET_free (dname); -} - - -struct TALER_MHD2_Legal * -TALER_MHD2_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - const char *diroption, - const char *tagoption) -{ - struct TALER_MHD2_Legal *legal; - char *path; - DIR *d; - - legal = GNUNET_new (struct TALER_MHD2_Legal); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - tagoption, - &legal->terms_version)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - section, - tagoption); - GNUNET_free (legal); - return NULL; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - section, - diroption, - &path)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING, - section, - diroption); - GNUNET_free (legal->terms_version); - GNUNET_free (legal); - return NULL; - } - d = opendir (path); - if (NULL == d) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING, - section, - diroption, - "Could not open directory"); - GNUNET_free (legal->terms_version); - GNUNET_free (legal); - GNUNET_free (path); - return NULL; - } - for (struct dirent *de = readdir (d); - NULL != de; - de = readdir (d)) - { - const char *lang = de->d_name; - - if (lang[0] == '.') - continue; - if (0 == strcmp (lang, - "locale")) - continue; - load_language (legal, - path, - lang); - } - GNUNET_break (0 == closedir (d)); - GNUNET_free (path); - return legal; -} - - -void -TALER_MHD2_legal_free (struct TALER_MHD2_Legal *legal) -{ - struct Terms *t; - if (NULL == legal) - return; - while (NULL != (t = legal->terms_head)) - { - GNUNET_free (t->language); - GNUNET_free (t->compressed_terms); - if (0 != munmap (t->terms, t->terms_size)) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "munmap"); - GNUNET_CONTAINER_DLL_remove (legal->terms_head, - legal->terms_tail, - t); - GNUNET_free (t->terms_etag); - GNUNET_free (t); - } - GNUNET_free (legal->terms_version); - GNUNET_free (legal); -} diff --git a/src/mhd/mhd2_responses.c b/src/mhd/mhd2_responses.c @@ -1,462 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file mhd2_responses.c - * @brief API for generating HTTP replies - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include <zlib.h> -#include "taler/taler_util.h" -#include "taler/taler_mhd2_lib.h" - - -/** - * Global options for response generation. - */ -static enum TALER_MHD2_GlobalOptions TM_go; - - -void -TALER_MHD2_setup (enum TALER_MHD2_GlobalOptions go) -{ - TM_go = go; -} - - -void -TALER_MHD2_add_global_headers (struct MHD_Response *response, - bool allow_store) -{ - if (0 != (TM_go & TALER_MHD2_GO_FORCE_CONNECTION_CLOSE)) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONNECTION, - "close")); - /* The wallet, operating from a background page, needs CORS to - be disabled otherwise browsers block access. */ - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, - "*")); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - /* Not available as MHD constant yet */ - "Access-Control-Expose-Headers", - "*")); - if (! allow_store) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CACHE_CONTROL, - "no-store")); -} - - -bool -TALER_MHD2_can_compress (struct MHD_Request *request) -{ - struct MHD_StringNullable aeb; - const char *ae; - const char *de; - - if (0 != (TM_go & TALER_MHD2_GO_DISABLE_COMPRESSION)) - return MHD_NO; - if ( (MHD_NO == - MHD_request_get_value (request, - MHD_VK_HEADER, - MHD_HTTP_HEADER_ACCEPT_ENCODING, - &aeb)) || - (NULL == aeb.cstr) ) - return false; - ae = aeb.cstr; - if (0 == strcmp (ae, - "*")) - return true; - de = strstr (ae, - "deflate"); - if (NULL == de) - return false; - if ( ( (de == ae) || - (de[-1] == ',') || - (de[-1] == ' ') ) && - ( (de[strlen ("deflate")] == '\0') || - (de[strlen ("deflate")] == ',') || - (de[strlen ("deflate")] == ';') ) ) - return true; - return false; -} - - -bool -TALER_MHD2_body_compress (void **buf, - size_t *buf_size) -{ - Bytef *cbuf; - uLongf cbuf_size; - int ret; - - cbuf_size = compressBound (*buf_size); - cbuf = malloc (cbuf_size); - if (NULL == cbuf) - return false; - ret = compress (cbuf, - &cbuf_size, - (const Bytef *) *buf, - *buf_size); - if ( (Z_OK != ret) || - (cbuf_size >= *buf_size) ) - { - /* compression failed */ - free (cbuf); - return false; - } - free (*buf); - *buf = (void *) cbuf; - *buf_size = (size_t) cbuf_size; - return true; -} - - -struct MHD_Response * -TALER_MHD2_make_json (enum MHD_HTTP_StatusCode sc, - const json_t *json) -{ - struct MHD_Response *response; - char *json_str; - - json_str = json_dumps (json, - JSON_INDENT (2)); - if (NULL == json_str) - { - GNUNET_break (0); - return NULL; - } - response = MHD_response_from_buffer (sc, - strlen (json_str), - json_str, - &free, - json_str); - if (NULL == response) - { - free (json_str); - GNUNET_break (0); - return NULL; - } - TALER_MHD2_add_global_headers (response, - false); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "application/json")); - return response; -} - - -struct MHD_Response * -TALER_MHD2_make_json_steal (enum MHD_HTTP_StatusCode sc, - json_t *json) -{ - struct MHD_Response *res; - - res = TALER_MHD2_make_json (sc, - json); - json_decref (json); - return res; -} - - -const struct MHD_Action * -TALER_MHD2_reply_json (struct MHD_Request *request, - const json_t *json, - enum MHD_HTTP_StatusCode sc) -{ - struct MHD_Response *response; - void *json_str; - size_t json_len; - bool is_compressed; - - json_str = json_dumps (json, - JSON_INDENT (2)); - if (NULL == json_str) - { - /** - * This log helps to figure out which - * function called this one and assert-failed. - */ - TALER_LOG_ERROR ("Aborting json-packing for HTTP code: %u\n", - sc); - - GNUNET_assert (0); - return MHD_action_abort_request (request); - } - json_len = strlen (json_str); - /* try to compress the body */ - is_compressed = MHD_NO; - if (TALER_MHD2_can_compress (request)) - is_compressed = TALER_MHD2_body_compress (&json_str, - &json_len); - response = MHD_response_from_buffer (sc, - json_len, - json_str, - &free, - json_str); - if (NULL == response) - { - free (json_str); - GNUNET_break (0); - return MHD_action_abort_request (request); - } - TALER_MHD2_add_global_headers (response, - false); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "application/json")); - if (is_compressed) - { - /* Need to indicate to client that body is compressed */ - if (MHD_SC_OK != - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) - { - GNUNET_break (0); - MHD_response_destroy (response); - return MHD_action_abort_request (request); - } - } - - return MHD_action_from_response (request, - response); -} - - -const struct MHD_Action * -TALER_MHD2_reply_json_steal (struct MHD_Request *request, - json_t *json, - enum MHD_HTTP_StatusCode sc) -{ - const struct MHD_Action *ret; - - ret = TALER_MHD2_reply_json (request, - json, - sc); - json_decref (json); - return ret; -} - - -const struct MHD_Action * -TALER_MHD2_reply_cors_preflight (struct MHD_Request *request) -{ - struct MHD_Response *response; - - response = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT); - if (NULL == response) - return MHD_action_abort_request (request); - /* This adds the Access-Control-Allow-Origin header. - * All endpoints of the exchange allow CORS. */ - TALER_MHD2_add_global_headers (response, - true); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - /* Not available as MHD constant yet */ - "Access-Control-Allow-Headers", - "*")); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - /* Not available as MHD constant yet */ - "Access-Control-Allow-Methods", - "*")); - return MHD_action_from_response (request, - response); -} - - -struct MHD_Response * -TALER_MHD2_make_error (enum TALER_ErrorCode ec, - const char *detail) -{ - return TALER_MHD2_MAKE_JSON_PACK ( - TALER_ErrorCode_get_http_status (ec), - TALER_MHD2_PACK_EC (ec), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("detail", detail))); -} - - -const struct MHD_Action * -TALER_MHD2_reply_with_error (struct MHD_Request *request, - enum MHD_HTTP_StatusCode sc, - enum TALER_ErrorCode ec, - const char *detail) -{ - return TALER_MHD2_REPLY_JSON_PACK ( - request, - sc, - TALER_MHD2_PACK_EC (ec), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("detail", detail))); -} - - -const struct MHD_Action * -TALER_MHD2_reply_with_ec (struct MHD_Request *request, - enum TALER_ErrorCode ec, - const char *detail) -{ - unsigned int hc - = TALER_ErrorCode_get_http_status (ec); - - if ( (0 == hc) || - (UINT_MAX == hc) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Invalid Taler error code %u provided for response!\n", - (unsigned int) ec); - hc = MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR; - } - return TALER_MHD2_reply_with_error (request, - (enum MHD_HTTP_StatusCode) hc, - ec, - detail); -} - - -const struct MHD_Action * -TALER_MHD2_reply_request_too_large (struct MHD_Request *request) -{ - return TALER_MHD2_reply_with_error ( - request, - MHD_HTTP_STATUS_CONTENT_TOO_LARGE, - TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT, - NULL); -} - - -const struct MHD_Action * -TALER_MHD2_reply_agpl (struct MHD_Request *request, - const char *url) -{ - const char *agpl = - "This server is licensed under the Affero GPL. You will now be redirected to the source code."; - struct MHD_Response *response; - - response = MHD_response_from_buffer_static (MHD_HTTP_STATUS_FOUND, - strlen (agpl), - (void *) agpl); - if (NULL == response) - { - GNUNET_break (0); - return MHD_action_abort_request (request); - } - TALER_MHD2_add_global_headers (response, - true); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "text/plain")); - if (MHD_SC_OK != - MHD_response_add_header (response, - MHD_HTTP_HEADER_LOCATION, - url)) - { - GNUNET_break (0); - MHD_response_destroy (response); - return MHD_action_abort_request (request); - } - return MHD_action_from_response (request, - response); -} - - -const struct MHD_Action * -TALER_MHD2_reply_static (struct MHD_Request *request, - enum MHD_HTTP_StatusCode sc, - const char *mime_type, - const char *body, - size_t body_size) -{ - struct MHD_Response *response; - - response = MHD_response_from_buffer_static (sc, - body_size, - body); - if (NULL == response) - { - GNUNET_break (0); - return MHD_action_abort_request (request); - } - TALER_MHD2_add_global_headers (response, - true); - if (NULL != mime_type) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - mime_type)); - return MHD_action_from_response (request, - response); -} - - -void -TALER_MHD2_get_date_string (struct GNUNET_TIME_Absolute at, - char date[128]) -{ - static const char *const days[] = - { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; - static const char *const mons[] = - { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", - "Nov", "Dec"}; - struct tm now; - time_t t; -#if ! defined(HAVE_C11_GMTIME_S) && ! defined(HAVE_W32_GMTIME_S) && \ - ! defined(HAVE_GMTIME_R) - struct tm*pNow; -#endif - - date[0] = 0; - t = (time_t) (at.abs_value_us / 1000LL / 1000LL); -#if defined(HAVE_C11_GMTIME_S) - if (NULL == gmtime_s (&t, &now)) - return; -#elif defined(HAVE_W32_GMTIME_S) - if (0 != gmtime_s (&now, &t)) - return; -#elif defined(HAVE_GMTIME_R) - if (NULL == gmtime_r (&t, &now)) - return; -#else - pNow = gmtime (&t); - if (NULL == pNow) - return; - now = *pNow; -#endif - sprintf (date, - "%3s, %02u %3s %04u %02u:%02u:%02u GMT", - days[now.tm_wday % 7], - (unsigned int) now.tm_mday, - mons[now.tm_mon % 12], - (unsigned int) (1900 + now.tm_year), - (unsigned int) now.tm_hour, - (unsigned int) now.tm_min, - (unsigned int) now.tm_sec); -} - - -/* end of mhd_responses.c */ diff --git a/src/mhd/mhd2_run.c b/src/mhd/mhd2_run.c @@ -1,414 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2019-2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU Affero General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. - - You should have received a copy of the GNU Affero General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file mhd2_run.c - * @brief API for running an MHD daemon with the - * GNUnet scheduler - * @author Christian Grothoff - */ -#include <gnunet/gnunet_util_lib.h> -#include <jansson.h> -#define MHD_APP_SOCKET_CNTX_TYPE struct SocketContext -#include <microhttpd2.h> -#include "taler/taler_mhd2_lib.h" - - -/** - * Context to track whatever MHD wants us to wait for. - */ -struct SocketContext -{ - /** - * Task for this socket. - */ - struct GNUNET_SCHEDULER_Task *mhd_rtask; - - /** - * Task for this socket. - */ - struct GNUNET_SCHEDULER_Task *mhd_wtask; - - /** - * Internal handle to pass to MHD when ready. - */ - struct MHD_EventUpdateContext *ecb_cntx; - - /** - * Socket to watch for. - */ - struct GNUNET_NETWORK_Handle *fd; - - /** - * Daemon this socket is about. - */ - struct DaemonEntry *de; -}; - - -/** - * Entry in list of HTTP servers we are running. - */ -struct DaemonEntry -{ - /** - * Kept in a DLL. - */ - struct DaemonEntry *next; - - /** - * Kept in a DLL. - */ - struct DaemonEntry *prev; - - /** - * The actual daemon. - */ - struct MHD_Daemon *mhd; - - /** - * Task running the HTTP server. - */ - struct GNUNET_SCHEDULER_Task *mhd_task; - - /** - * Task waiting for timeout on the HTTP server. - */ - struct GNUNET_SCHEDULER_Task *timeout_task; - - /** - * Set to true if we should immediately MHD_run() again. - */ - bool triggered; - -}; - - -/** - * Head of list of HTTP servers. - */ -static struct DaemonEntry *mhd_head; - -/** - * Tail of list of HTTP servers. - */ -static struct DaemonEntry *mhd_tail; - - -/** - * Function that queries MHD's select sets and - * starts the task waiting for them. - * - * @param[in,out] de daemon to start tasks for - */ -static void -prepare_daemon (struct DaemonEntry *de); - - -/** - * Trigger MHD on timeout. - * - * @param cls a `struct DaemonEntry` - */ -static void -handle_timeout (void *cls) -{ - struct DaemonEntry *de = cls; - - de->timeout_task = NULL; - prepare_daemon (de); -} - - -static void -prepare_daemon (struct DaemonEntry *de) -{ - uint_fast64_t next_max_wait; - enum MHD_StatusCode sc; - - sc = MHD_daemon_process_reg_events (de->mhd, - &next_max_wait); - if (MHD_SC_OK != sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "MHD_daemon_process_reg_events failed: %d\n", - (int) sc); - return; - } - if (MHD_WAIT_INDEFINITELY == next_max_wait) - return; - de->timeout_task - = GNUNET_SCHEDULER_add_delayed ( - GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_MICROSECONDS, - next_max_wait), - &handle_timeout, - de); -} - - -/** - * Call MHD to process pending requests and then go back - * and schedule the next run. - * - * @param cls our `struct DaemonEntry *` - */ -static void -run_daemon (void *cls) -{ - struct DaemonEntry *de = cls; - - de->mhd_task = NULL; - prepare_daemon (de); -} - - -/** - * Called whenever MHD should process read-events on the socket. - * - * @param cls a `struct SocketContext` - */ -static void -mhd_rready (void *cls) -{ - struct SocketContext *sc = cls; - struct DaemonEntry *de = sc->de; - - sc->mhd_rtask = NULL; - MHD_daemon_event_update (de->mhd, - sc->ecb_cntx, - MHD_FD_STATE_RECV); - if (NULL != de->mhd_task) - GNUNET_SCHEDULER_cancel (de->mhd_task); - de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon, - de); -} - - -/** - * Called whenever MHD should process write-events on the socket. - * - * @param cls a `struct SocketContext` - */ -static void -mhd_wready (void *cls) -{ - struct SocketContext *sc = cls; - struct DaemonEntry *de = sc->de; - - sc->mhd_wtask = NULL; - MHD_daemon_event_update (de->mhd, - sc->ecb_cntx, - MHD_FD_STATE_SEND); - if (NULL != de->mhd_task) - GNUNET_SCHEDULER_cancel (de->mhd_task); - de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon, - de); -} - - -/** - * Callback for registration/de-registration of the sockets to watch. - * - * @param cls our `struct DaemonEntry` - * @param fd the socket to watch - * @param watch_for the states of the @a fd to watch, if set to - * #MHD_FD_STATE_NONE the socket must be de-registred - * @param app_cntx_old the old application defined context for the socket, - * NULL if @a fd socket was not registered before - * @param ecb_cntx the context handle to be used - * with #MHD_daemon_event_update() - * @return NULL if error (to connection will be aborted), - * or the new socket context - */ -static MHD_APP_SOCKET_CNTX_TYPE * -socket_registration_update ( - void *cls, - MHD_Socket fd, - enum MHD_FdState watch_for, - MHD_APP_SOCKET_CNTX_TYPE *app_cntx_old, - struct MHD_EventUpdateContext *ecb_cntx) -{ - struct DaemonEntry *de = cls; - - if (NULL == app_cntx_old) - { - app_cntx_old = GNUNET_new (struct SocketContext); - app_cntx_old->ecb_cntx = ecb_cntx; - app_cntx_old->fd = GNUNET_NETWORK_socket_box_native (fd); - app_cntx_old->de = de; - } - if (MHD_FD_STATE_NONE == watch_for) - { - if (NULL != app_cntx_old->mhd_rtask) - GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_rtask); - if (NULL != app_cntx_old->mhd_wtask) - GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_wtask); - GNUNET_NETWORK_socket_free_memory_only_ (app_cntx_old->fd); - GNUNET_free (app_cntx_old); - return NULL; - } - if ( (MHD_FD_STATE_RECV & watch_for) && - (NULL == app_cntx_old->mhd_rtask) ) - { - app_cntx_old->mhd_rtask - = GNUNET_SCHEDULER_add_read_net (GNUNET_TIME_UNIT_FOREVER_REL, - app_cntx_old->fd, - &mhd_rready, - app_cntx_old); - } - if ( (MHD_FD_STATE_SEND & watch_for) && - (NULL == app_cntx_old->mhd_wtask) ) - { - app_cntx_old->mhd_wtask - = GNUNET_SCHEDULER_add_write_net (GNUNET_TIME_UNIT_FOREVER_REL, - app_cntx_old->fd, - &mhd_wready, - app_cntx_old); - } - if ( (0 == (MHD_FD_STATE_RECV & watch_for)) && - (NULL != app_cntx_old->mhd_rtask) ) - { - GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_rtask); - app_cntx_old->mhd_rtask = NULL; - } - if ( (0 == (MHD_FD_STATE_SEND & watch_for)) && - (NULL != app_cntx_old->mhd_wtask) ) - { - GNUNET_SCHEDULER_cancel (app_cntx_old->mhd_wtask); - app_cntx_old->mhd_wtask = NULL; - } - return app_cntx_old; -} - - -void -TALER_MHD2_daemon_start (struct MHD_Daemon *daemon) -{ - struct DaemonEntry *de; - enum MHD_StatusCode sc; - - de = GNUNET_new (struct DaemonEntry); - GNUNET_assert (MHD_SC_OK == - MHD_DAEMON_SET_OPTIONS ( - daemon, - MHD_D_OPTION_REREGISTER_ALL (true), - MHD_D_OPTION_WM_EXTERNAL_EVENT_LOOP_CB_LEVEL ( - &socket_registration_update, - de))); - de->mhd = daemon; - sc = MHD_daemon_start (de->mhd); - if (MHD_SC_OK != sc) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "MHD_daemon_start failed: %d\n", - (int) sc); - GNUNET_free (de); - return; - } - GNUNET_CONTAINER_DLL_insert (mhd_head, - mhd_tail, - de); - prepare_daemon (de); -} - - -void -TALER_MHD2_daemons_halt (void) -{ - for (struct DaemonEntry *de = mhd_head; - NULL != de; - de = de->next) - { - if (NULL != de->mhd_task) - { - GNUNET_SCHEDULER_cancel (de->mhd_task); - de->mhd_task = NULL; - } - de->triggered = false; - } -} - - -void -TALER_MHD2_daemons_quiesce (void) -{ - for (struct DaemonEntry *de = mhd_head; - NULL != de; - de = de->next) - { -#if FIXME_MHD2 - int fd; -#endif - - if (NULL != de->mhd_task) - { - GNUNET_SCHEDULER_cancel (de->mhd_task); - de->mhd_task = NULL; - } - de->triggered = false; -#if FIXME_MHD2 - fd = MHD_daemon_quiesce (de->mhd); - GNUNET_break (0 == close (fd)); -#endif - } -} - - -void -TALER_MHD2_daemons_destroy (void) -{ - struct DaemonEntry *de; - - while (NULL != (de = mhd_head)) - { - struct MHD_Daemon *mhd = de->mhd; - - if (NULL != de->mhd_task) - { - GNUNET_SCHEDULER_cancel (de->mhd_task); - de->mhd_task = NULL; - } - MHD_daemon_destroy (mhd); - GNUNET_CONTAINER_DLL_remove (mhd_head, - mhd_tail, - de); - GNUNET_free (de); - } -} - - -void -TALER_MHD2_daemons_trigger (void) -{ - for (struct DaemonEntry *de = mhd_head; - NULL != de; - de = de->next) - { - if (NULL != de->mhd_task) - { - GNUNET_SCHEDULER_cancel (de->mhd_task); - de->mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon, - de); - } - else - { - de->triggered = true; - } - } -} - - -/* end of mhd2_run.c */ diff --git a/src/mhd/mhd2_spa.c b/src/mhd/mhd2_spa.c @@ -1,390 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020--2025 Taler Systems SA - - TALER is free software; you can redistribute it and/or modify it under the - terms of the GNU General Public License as published by the Free Software - Foundation; either version 3, or (at your option) any later version. - - TALER is distributed in the hope that it will be useful, but WITHOUT ANY - WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR - A PARTICULAR PURPOSE. See the GNU General Public License for more details. - - You should have received a copy of the GNU General Public License along with - TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file mhd2_spa.c - * @brief logic to load single page apps - * @author Christian Grothoff - */ -#include "platform.h" /* UNNECESSARY? */ -#include <gnunet/gnunet_util_lib.h> -#include "taler/taler_util.h" -#include "taler/taler_mhd2_lib.h" - - -/** - * Resource from the WebUi. - */ -struct WebuiFile -{ - /** - * Kept in a DLL. - */ - struct WebuiFile *next; - - /** - * Kept in a DLL. - */ - struct WebuiFile *prev; - - /** - * Path this resource matches. - */ - char *path; - - /** - * SPA resource, compressed. - */ - struct MHD_Response *zresponse; - - /** - * SPA resource, vanilla. - */ - struct MHD_Response *response; - -}; - - -/** - * Resource from the WebUi. - */ -struct TALER_MHD2_Spa -{ - /** - * Resources of the WebUI, kept in a DLL. - */ - struct WebuiFile *webui_head; - - /** - * Resources of the WebUI, kept in a DLL. - */ - struct WebuiFile *webui_tail; -}; - - -const struct MHD_Action * -TALER_MHD2_spa_handler (const struct TALER_MHD2_Spa *spa, - struct MHD_Request *request, - const char *path) -{ - struct WebuiFile *w = NULL; - - if ( (NULL == path) || - (0 == strcmp (path, - "")) ) - path = "index.html"; - for (struct WebuiFile *pos = spa->webui_head; - NULL != pos; - pos = pos->next) - if (0 == strcmp (path, - pos->path)) - { - w = pos; - break; - } - if (NULL == w) - return TALER_MHD2_reply_with_error (request, - MHD_HTTP_STATUS_NOT_FOUND, - TALER_EC_GENERIC_ENDPOINT_UNKNOWN, - path); - if ( (MHD_YES == - TALER_MHD2_can_compress (request)) && - (NULL != w->zresponse) ) - return MHD_action_from_response (request, - w->zresponse); - return MHD_action_from_response (request, - w->response); -} - - -/** - * Function called on each file to load for the WebUI. - * - * @param cls the `struct TALER_MHD2_Spa *` to build - * @param dn name of the file to load - */ -static enum GNUNET_GenericReturnValue -build_webui (void *cls, - const char *dn) -{ - static const struct - { - const char *ext; - const char *mime; - } mime_map[] = { - { - .ext = "css", - .mime = "text/css" - }, - { - .ext = "html", - .mime = "text/html" - }, - { - .ext = "js", - .mime = "text/javascript" - }, - { - .ext = "jpg", - .mime = "image/jpeg" - }, - { - .ext = "jpeg", - .mime = "image/jpeg" - }, - { - .ext = "png", - .mime = "image/png" - }, - { - .ext = "svg", - .mime = "image/svg+xml" - }, - { - .ext = NULL, - .mime = NULL - }, - }; - struct TALER_MHD2_Spa *spa = cls; - int fd; - struct stat sb; - struct MHD_Response *zresponse = NULL; - struct MHD_Response *response; - const char *ext; - const char *mime; - - fd = open (dn, - O_RDONLY); - if (-1 == fd) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "open", - dn); - return GNUNET_SYSERR; - } - if (0 != - fstat (fd, - &sb)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "open", - dn); - GNUNET_break (0 == close (fd)); - return GNUNET_SYSERR; - } - - mime = NULL; - ext = strrchr (dn, '.'); - if (NULL == ext) - { - GNUNET_break (0 == close (fd)); - return GNUNET_OK; - } - ext++; - for (unsigned int i = 0; NULL != mime_map[i].ext; i++) - if (0 == strcasecmp (ext, - mime_map[i].ext)) - { - mime = mime_map[i].mime; - break; - } - - { - void *in; - ssize_t r; - size_t csize; - - in = GNUNET_malloc_large (sb.st_size); - if (NULL == in) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "malloc"); - GNUNET_break (0 == close (fd)); - return GNUNET_SYSERR; - } - r = read (fd, - in, - sb.st_size); - if ( (-1 == r) || - (sb.st_size != (size_t) r) ) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "read", - dn); - GNUNET_free (in); - GNUNET_break (0 == close (fd)); - return GNUNET_SYSERR; - } - csize = (size_t) r; - if (TALER_MHD2_body_compress (&in, - &csize)) - { - zresponse = MHD_response_from_buffer (MHD_HTTP_STATUS_OK, - csize, - in, - &free, - in); - if (NULL != zresponse) - { - if (MHD_SC_OK != - MHD_response_add_header (zresponse, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) - { - GNUNET_break (0); - MHD_response_destroy (zresponse); - zresponse = NULL; - } - else if (NULL != mime) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (zresponse, - MHD_HTTP_HEADER_CONTENT_TYPE, - mime)); - /* Which representation we return depends on the - Accept-Encoding header of the client, so shared caches - must not serve one variant to a client that asked for - another. */ - if (NULL != zresponse) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (zresponse, - MHD_HTTP_HEADER_VARY, - MHD_HTTP_HEADER_ACCEPT_ENCODING)); - } - } - else - { - GNUNET_free (in); - } - } - - response = MHD_response_from_fd (MHD_HTTP_STATUS_OK, - fd, - 0LLU, - sb.st_size); - if (NULL == response) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "open", - dn); - GNUNET_break (0 == close (fd)); - if (NULL != zresponse) - { - MHD_response_destroy (zresponse); - zresponse = NULL; - } - return GNUNET_SYSERR; - } - if (NULL != mime) - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - mime)); - GNUNET_break (MHD_SC_OK == - MHD_response_add_header (response, - MHD_HTTP_HEADER_VARY, - MHD_HTTP_HEADER_ACCEPT_ENCODING)); - - { - struct WebuiFile *w; - const char *fn; - - fn = strrchr (dn, '/'); - GNUNET_assert (NULL != fn); - w = GNUNET_new (struct WebuiFile); - w->path = GNUNET_strdup (fn + 1); - w->response = response; - w->zresponse = zresponse; - GNUNET_assert (MHD_SC_OK == - MHD_RESPONSE_SET_OPTIONS (response, - MHD_R_OPTION_REUSABLE (MHD_YES))); - if (NULL != zresponse) - GNUNET_assert (MHD_SC_OK == - MHD_RESPONSE_SET_OPTIONS (zresponse, - MHD_R_OPTION_REUSABLE (MHD_YES))) - ; - GNUNET_CONTAINER_DLL_insert (spa->webui_head, - spa->webui_tail, - w); - } - return GNUNET_OK; -} - - -struct TALER_MHD2_Spa * -TALER_MHD2_spa_load (const struct GNUNET_OS_ProjectData *pd, - const char *dir) -{ - struct TALER_MHD2_Spa *spa; - char *dn; - - { - char *path; - - path = GNUNET_OS_installation_get_path (pd, - GNUNET_OS_IPK_DATADIR); - if (NULL == path) - { - GNUNET_break (0); - return NULL; - } - GNUNET_asprintf (&dn, - "%s%s", - path, - dir); - GNUNET_free (path); - } - spa = GNUNET_new (struct TALER_MHD2_Spa); - if (-1 == - GNUNET_DISK_directory_scan (dn, - &build_webui, - spa)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to load WebUI from `%s'\n", - dn); - GNUNET_free (dn); - TALER_MHD2_spa_free (spa); - return NULL; - } - GNUNET_free (dn); - return spa; -} - - -void -TALER_MHD2_spa_free (struct TALER_MHD2_Spa *spa) -{ - struct WebuiFile *w; - - while (NULL != (w = spa->webui_head)) - { - GNUNET_CONTAINER_DLL_remove (spa->webui_head, - spa->webui_tail, - w); - if (NULL != w->response) - { - MHD_response_destroy (w->response); - w->response = NULL; - } - if (NULL != w->zresponse) - { - MHD_response_destroy (w->zresponse); - w->zresponse = NULL; - } - GNUNET_free (w->path); - GNUNET_free (w); - } - GNUNET_free (spa); -}