donau

Donation authority for GNU Taler (experimental)
Log | Files | Refs | Submodules | README | LICENSE

commit 45ed194289863d85b3f19cfb5cbb53664634b5e7
parent a3c666dc9867f852fc3adf262aede52b54ea2c0f
Author: Christian Grothoff <christian@grothoff.org>
Date:   Tue, 24 Oct 2023 10:45:31 +0200

clean up

Diffstat:
M.gitignore | 1+
Msrc/Makefile.am | 2--
Dsrc/curl/Makefile.am | 24------------------------
Dsrc/curl/curl.c | 107-------------------------------------------------------------------------------
Msrc/json/Makefile.am | 4+---
Dsrc/json/i18n.c | 134-------------------------------------------------------------------------------
Dsrc/json/json_wire.c | 122-------------------------------------------------------------------------------
Dsrc/mhd/Makefile.am | 29-----------------------------
Dsrc/mhd/mhd_config.c | 493-------------------------------------------------------------------------------
Dsrc/mhd/mhd_legal.c | 694-------------------------------------------------------------------------------
Dsrc/mhd/mhd_parsing.c | 444-------------------------------------------------------------------------------
Dsrc/mhd/mhd_responses.c | 550-------------------------------------------------------------------------------
Dsrc/mhd/mhd_run.c | 175-------------------------------------------------------------------------------
Dsrc/sq/Makefile.am | 40----------------------------------------
Dsrc/sq/sq_query_helper.c | 175-------------------------------------------------------------------------------
Dsrc/sq/sq_result_helper.c | 237-------------------------------------------------------------------------------
Dsrc/sq/test_sq.c | 215-------------------------------------------------------------------------------
17 files changed, 2 insertions(+), 3444 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -1,3 +1,4 @@ +**/Makefile Makefile.in autom4te.cache/ build-aux/ diff --git a/src/Makefile.am b/src/Makefile.am @@ -15,9 +15,7 @@ SUBDIRS = \ include \ util \ json \ - curl \ $(PQ_DIR) \ - mhd \ donaudb \ donau \ lib \ diff --git a/src/curl/Makefile.am b/src/curl/Makefile.am @@ -1,24 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - -lib_LTLIBRARIES = \ - libtalercurl.la - -libtalercurl_la_LDFLAGS = \ - -version-info 0:0:0 \ - -no-undefined -libtalercurl_la_SOURCES = \ - curl.c -libtalercurl_la_LIBADD = \ - -lgnunetcurl \ - -lgnunetutil \ - $(LIBGNURLCURL_LIBS) \ - -ljansson \ - -lz \ - -lm \ - $(XLIB) diff --git a/src/curl/curl.c b/src/curl/curl.c @@ -1,107 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2019-2021 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 curl/curl.c - * @brief Helper routines for interactions with libcurl - * @author Christian Grothoff - */ -#include "platform.h" -#include "taler_curl_lib.h" - - -#if TALER_CURL_COMPRESS_BODIES -#include <zlib.h> -#endif - - -enum GNUNET_GenericReturnValue -TALER_curl_easy_post (struct TALER_CURL_PostContext *ctx, - CURL *eh, - const json_t *body) -{ - char *str; - size_t slen; - - str = json_dumps (body, - JSON_COMPACT); - if (NULL == str) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - slen = strlen (str); - if (TALER_CURL_COMPRESS_BODIES && - (! ctx->disable_compression) ) - { - Bytef *cbuf; - uLongf cbuf_size; - int ret; - - cbuf_size = compressBound (slen); - cbuf = GNUNET_malloc (cbuf_size); - ret = compress (cbuf, - &cbuf_size, - (const Bytef *) str, - slen); - if (Z_OK != ret) - { - /* compression failed!? */ - GNUNET_break (0); - GNUNET_free (cbuf); - return GNUNET_SYSERR; - } - free (str); - slen = (size_t) cbuf_size; - ctx->json_enc = (char *) cbuf; - GNUNET_assert ( - NULL != - (ctx->headers = curl_slist_append ( - ctx->headers, - "Content-Encoding: deflate"))); - } - else - { - ctx->json_enc = str; - } - GNUNET_assert ( - NULL != - (ctx->headers = curl_slist_append ( - ctx->headers, - "Content-Type: application/json"))); - - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDS, - ctx->json_enc)); - GNUNET_assert (CURLE_OK == - curl_easy_setopt (eh, - CURLOPT_POSTFIELDSIZE, - slen)); - return GNUNET_OK; -} - - -void -TALER_curl_easy_post_finished (struct TALER_CURL_PostContext *ctx) -{ - curl_slist_free_all (ctx->headers); - ctx->headers = NULL; - GNUNET_free (ctx->json_enc); - ctx->json_enc = NULL; -} diff --git a/src/json/Makefile.am b/src/json/Makefile.am @@ -10,11 +10,9 @@ lib_LTLIBRARIES = \ libtalerjson.la libtalerjson_la_SOURCES = \ - i18n.c \ json.c \ json_helper.c \ - json_pack.c \ - json_wire.c + json_pack.c libtalerjson_la_LDFLAGS = \ -version-info 1:0:1 \ -no-undefined diff --git a/src/json/i18n.c b/src/json/i18n.c @@ -1,134 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020, 2021 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 json/i18n.c - * @brief helper functions for i18n in JSON processing - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include "taler_util.h" -#include "taler_json_lib.h" - - -const json_t * -TALER_JSON_extract_i18n (const json_t *object, - const char *language_pattern, - const char *field) -{ - const json_t *ret; - json_t *i18n; - double quality = -1; - - ret = json_object_get (object, - field); - if (NULL == ret) - return NULL; /* field MUST exist in object */ - { - char *name; - - GNUNET_asprintf (&name, - "%s_i18n", - field); - i18n = json_object_get (object, - name); - GNUNET_free (name); - } - if (NULL == i18n) - return ret; - { - const char *key; - json_t *value; - - json_object_foreach (i18n, key, value) { - double q = TALER_language_matches (language_pattern, - key); - if (q > quality) - { - quality = q; - ret = value; - } - } - } - return ret; -} - - -bool -TALER_JSON_check_i18n (const json_t *i18n) -{ - const char *field; - json_t *member; - - if (! json_is_object (i18n)) - return false; - json_object_foreach ((json_t *) i18n, field, member) - { - if (! json_is_string (member)) - return false; - /* Field name must be either of format "en_UK" - or just "en"; we do not care about capitalization; - for syntax, see GNU Gettext manual, including - appendix A for rare language codes. */ - switch (strlen (field)) - { - case 0: - case 1: - return false; - case 2: - if (! isalpha (field[0])) - return false; - if (! isalpha (field[1])) - return false; - break; - case 3: - case 4: - return false; - case 5: - if (! isalpha (field[0])) - return false; - if (! isalpha (field[1])) - return false; - if ('_' != field[2]) - return false; - if (! isalpha (field[3])) - return false; - if (! isalpha (field[4])) - return false; - break; - case 6: - if (! isalpha (field[0])) - return false; - if (! isalpha (field[1])) - return false; - if ('_' != field[2]) - return false; - if (! isalpha (field[3])) - return false; - if (! isalpha (field[4])) - return false; - if (! isalpha (field[5])) - return false; - break; - default: - return false; - } - } - return true; -} - - -/* end of i18n.c */ diff --git a/src/json/json_wire.c b/src/json/json_wire.c @@ -1,122 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2018, 2021 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 json/json_wire.c - * @brief helper functions to generate or check /wire replies - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include "taler_util.h" -#include "taler_json_lib.h" - - -enum GNUNET_GenericReturnValue -TALER_JSON_merchant_wire_signature_hash (const json_t *wire_s, - struct TALER_MerchantWireHashP *hc) -{ - const char *payto_uri; - struct TALER_WireSaltP salt; - struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_string ("payto_uri", - &payto_uri), - GNUNET_JSON_spec_fixed_auto ("salt", - &salt), - GNUNET_JSON_spec_end () - }; - - if (GNUNET_OK != - GNUNET_JSON_parse (wire_s, - spec, - NULL, NULL)) - { - GNUNET_break_op (0); - return GNUNET_SYSERR; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Validating `%s'\n", - payto_uri); - { - char *err; - - err = TALER_payto_validate (payto_uri); - if (NULL != err) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "URI `%s' ill-formed: %s\n", - payto_uri, - err); - GNUNET_free (err); - return GNUNET_SYSERR; - } - } - TALER_merchant_wire_signature_hash (payto_uri, - &salt, - hc); - return GNUNET_OK; -} - - -char * -TALER_JSON_wire_to_payto (const json_t *wire_s) -{ - json_t *payto_o; - const char *payto_str; - char *err; - - payto_o = json_object_get (wire_s, - "payto_uri"); - if ( (NULL == payto_o) || - (NULL == (payto_str = json_string_value (payto_o))) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Malformed wire record encountered: lacks payto://-url\n"); - return NULL; - } - if (NULL != - (err = TALER_payto_validate (payto_str))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Malformed wire record encountered: payto URI `%s' invalid: %s\n", - payto_str, - err); - GNUNET_free (err); - return NULL; - } - return GNUNET_strdup (payto_str); -} - - -char * -TALER_JSON_wire_to_method (const json_t *wire_s) -{ - json_t *payto_o; - const char *payto_str; - - payto_o = json_object_get (wire_s, - "payto_uri"); - if ( (NULL == payto_o) || - (NULL == (payto_str = json_string_value (payto_o))) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Fatally malformed wire record encountered: lacks payto://-url\n"); - return NULL; - } - return TALER_payto_get_method (payto_str); -} - - -/* end of json_wire.c */ diff --git a/src/mhd/Makefile.am b/src/mhd/Makefile.am @@ -1,29 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - -lib_LTLIBRARIES = \ - libtalermhd.la - -libtalermhd_la_SOURCES = \ - mhd_config.c \ - mhd_legal.c \ - mhd_parsing.c \ - mhd_responses.c \ - mhd_run.c -libtalermhd_la_LDFLAGS = \ - -version-info 0:0:0 \ - -no-undefined -libtalermhd_la_LIBADD = \ - -lgnunetjson \ - $(top_builddir)/src/json/libtalerjson.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetutil \ - -lmicrohttpd \ - -ljansson \ - -lz \ - $(XLIB) diff --git a/src/mhd/mhd_config.c b/src/mhd/mhd_config.c @@ -1,493 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014--2020 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 mhd_config.c - * @brief functions to configure and setup MHD - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include "taler_mhd_lib.h" - - -/** - * Backlog for listen operation on UNIX domain sockets. - */ -#define UNIX_BACKLOG 500 - - -/** - * Parse the configuration to determine on which port - * or UNIX domain path we should run an HTTP service. - * - * @param cfg configuration to parse - * @param section section of the configuration to parse (usually "exchange") - * @param[out] rport set to the port number, or 0 for none - * @param[out] unix_path set to the UNIX path, or NULL for none - * @param[out] unix_mode set to the mode to be used for @a unix_path - * @return #GNUNET_OK on success - */ -enum GNUNET_GenericReturnValue -TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - uint16_t *rport, - char **unix_path, - mode_t *unix_mode) -{ - const char *choices[] = { - "tcp", - "unix", - NULL - }; - const char *serve_type; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_choice (cfg, - section, - "SERVE", - choices, - &serve_type)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "SERVE", - "serve type (tcp or unix) required"); - return GNUNET_SYSERR; - } - - if (0 == strcasecmp (serve_type, - "tcp")) - { - unsigned long long port; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_number (cfg, - section, - "PORT", - &port)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "PORT", - "port number required"); - return GNUNET_SYSERR; - } - - if ( (0 == port) || - (port > UINT16_MAX) ) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "PORT", - "port number not in [1,65535]"); - return GNUNET_SYSERR; - } - *rport = (uint16_t) port; - *unix_path = NULL; - return GNUNET_OK; - } - if (0 == strcmp (serve_type, - "unix")) - { - struct sockaddr_un s_un; - char *modestring; - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_filename (cfg, - section, - "UNIXPATH", - unix_path)) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "UNIXPATH", - "UNIXPATH value required"); - return GNUNET_SYSERR; - } - if (strlen (*unix_path) >= sizeof (s_un.sun_path)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "unixpath `%s' is too long\n", - *unix_path); - GNUNET_free (*unix_path); - return GNUNET_SYSERR; - } - - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "UNIXPATH_MODE", - &modestring)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "UNIXPATH_MODE"); - GNUNET_free (*unix_path); - return GNUNET_SYSERR; - } - errno = 0; - *unix_mode = (mode_t) strtoul (modestring, NULL, 8); - if (0 != errno) - { - GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, - section, - "UNIXPATH_MODE", - "must be octal number"); - GNUNET_free (modestring); - GNUNET_free (*unix_path); - return GNUNET_SYSERR; - } - GNUNET_free (modestring); - return GNUNET_OK; - } - /* not reached */ - GNUNET_assert (0); - return GNUNET_SYSERR; -} - - -/** - * 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) -{ - static int cache; - char buf[2048]; - - (void) cls; - if (-1 == cache) - return; - if (0 == cache) - { - if (0 == - GNUNET_get_log_call_status (GNUNET_ERROR_TYPE_INFO, - "libmicrohttpd", - __FILE__, - __FUNCTION__, - __LINE__)) - { - cache = -1; - return; - } - } - cache = 1; - vsnprintf (buf, - sizeof (buf), - fm, - ap); - GNUNET_log_from_nocheck (GNUNET_ERROR_TYPE_INFO, - "libmicrohttpd", - "%s", - buf); -} - - -/** - * Open UNIX domain socket for listining at @a unix_path with - * permissions @a unix_mode. - * - * @param unix_path where to listen - * @param unix_mode access permissions to set - * @return -1 on error, otherwise the listen socket - */ -int -TALER_MHD_open_unix_path (const char *unix_path, - mode_t unix_mode) -{ - struct GNUNET_NETWORK_Handle *nh; - struct sockaddr_un *un; - - if (sizeof (un->sun_path) <= strlen (unix_path)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "unixpath `%s' is too long\n", - unix_path); - return -1; - } - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Creating listen socket '%s' with mode %o\n", - unix_path, - unix_mode); - - if (GNUNET_OK != - GNUNET_DISK_directory_create_for_file (unix_path)) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "mkdir", - unix_path); - } - - un = GNUNET_new (struct sockaddr_un); - un->sun_family = AF_UNIX; - strncpy (un->sun_path, - unix_path, - sizeof (un->sun_path) - 1); - GNUNET_NETWORK_unix_precheck (un); - - if (NULL == (nh = GNUNET_NETWORK_socket_create (AF_UNIX, - SOCK_STREAM, - 0))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "socket"); - GNUNET_free (un); - return -1; - } - - if (GNUNET_OK != - GNUNET_NETWORK_socket_bind (nh, - (void *) un, - sizeof (struct sockaddr_un))) - { - GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "bind", - unix_path); - GNUNET_free (un); - GNUNET_NETWORK_socket_close (nh); - return -1; - } - GNUNET_free (un); - if (GNUNET_OK != - GNUNET_NETWORK_socket_listen (nh, - UNIX_BACKLOG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "listen"); - GNUNET_NETWORK_socket_close (nh); - return -1; - } - - if (0 != chmod (unix_path, - unix_mode)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "chmod"); - GNUNET_NETWORK_socket_close (nh); - return -1; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "set socket '%s' to mode %o\n", - unix_path, - unix_mode); - - /* extract and return actual socket handle from 'nh' */ - { - int fd; - - fd = GNUNET_NETWORK_get_fd (nh); - GNUNET_NETWORK_socket_free_memory_only_ (nh); - return fd; - } -} - - -/** - * Bind a listen socket to the UNIX domain path or the TCP port and IP address - * as specified in @a cfg in section @a section. IF only a port was - * specified, set @a port and return -1. Otherwise, return the bound file - * descriptor. - * - * @param cfg configuration to parse - * @param section configuration section to use - * @param[out] port port to set, if TCP without BINDTO - * @return -1 and a port of zero on error, otherwise - * either -1 and a port, or a bound stream socket - */ -int -TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - uint16_t *port) -{ - char *bind_to; - struct GNUNET_NETWORK_Handle *nh; - - /* try systemd passing first */ - { - const char *listen_pid; - const char *listen_fds; - - /* check for systemd-style FD passing */ - listen_pid = getenv ("LISTEN_PID"); - listen_fds = getenv ("LISTEN_FDS"); - if ( (NULL != listen_pid) && - (NULL != listen_fds) && - (getpid () == strtol (listen_pid, - NULL, - 10)) && - (1 == strtoul (listen_fds, - NULL, - 10)) ) - { - int fh; - int flags; - - fh = 3; - flags = fcntl (fh, - F_GETFD); - if ( (-1 == flags) && - (EBADF == errno) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Bad listen socket passed, ignored\n"); - fh = -1; - } - flags |= FD_CLOEXEC; - if ( (-1 != fh) && - (0 != fcntl (fh, - F_SETFD, - flags)) ) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "fcntl"); - if (-1 != fh) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Successfully obtained listen socket from hypervisor\n"); - return fh; - } - } - } - - /* now try configuration file */ - *port = 0; - { - char *serve_unixpath; - mode_t unixpath_mode; - - if (GNUNET_OK != - TALER_MHD_parse_config (cfg, - section, - port, - &serve_unixpath, - &unixpath_mode)) - return -1; - if (NULL != serve_unixpath) - { - int ret; - - ret = TALER_MHD_open_unix_path (serve_unixpath, - unixpath_mode); - GNUNET_free (serve_unixpath); - return ret; - } - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "BIND_TO", - &bind_to)) - return -1; /* only set port */ - - /* let's have fun binding... */ - { - char port_str[6]; - struct addrinfo hints; - struct addrinfo *res; - int ec; - - GNUNET_snprintf (port_str, - sizeof (port_str), - "%u", - (unsigned int) *port); - *port = 0; /* do NOT return port in case of errors */ - memset (&hints, - 0, - sizeof (hints)); - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = SOCK_STREAM; - hints.ai_protocol = IPPROTO_TCP; - hints.ai_flags = AI_PASSIVE -#ifdef AI_IDN - | AI_IDN -#endif - ; - - if (0 != - (ec = getaddrinfo (bind_to, - port_str, - &hints, - &res))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to resolve BIND_TO address `%s': %s\n", - bind_to, - gai_strerror (ec)); - GNUNET_free (bind_to); - return -1; - } - GNUNET_free (bind_to); - - if (NULL == (nh = GNUNET_NETWORK_socket_create (res->ai_family, - res->ai_socktype, - res->ai_protocol))) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "socket"); - freeaddrinfo (res); - return -1; - } - { - const int on = 1; - - if (GNUNET_OK != - GNUNET_NETWORK_socket_setsockopt (nh, - SOL_SOCKET, - SO_REUSEPORT, - &on, - sizeof(on))) - GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING, - "setsockopt"); - } - if (GNUNET_OK != - GNUNET_NETWORK_socket_bind (nh, - res->ai_addr, - res->ai_addrlen)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "bind"); - freeaddrinfo (res); - return -1; - } - freeaddrinfo (res); - } - - if (GNUNET_OK != - GNUNET_NETWORK_socket_listen (nh, - UNIX_BACKLOG)) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "listen"); - GNUNET_SCHEDULER_shutdown (); - return -1; - } - - /* extract and return actual socket handle from 'nh' */ - { - int fh; - - fh = GNUNET_NETWORK_get_fd (nh); - GNUNET_NETWORK_socket_free_memory_only_ (nh); - return fh; - } -} diff --git a/src/mhd/mhd_legal.c b/src/mhd/mhd_legal.c @@ -1,694 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2019, 2020, 2022 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 mhd_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 <microhttpd.h> -#include "taler_util.h" -#include "taler_mhd_lib.h" - -/** - * How long should browsers/proxies cache the "legal" replies? - */ -#define MAX_TERMS_CACHING GNUNET_TIME_UNIT_DAYS - - -/** - * 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; - - /** - * 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_MHD_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_etag; -}; - - -/** - * Check if @a mime matches the @a accept_pattern. - * - * @param accept_pattern a mime pattern like "text/plain" - * or "image/STAR" - * @param mime the mime type to match - * @return true if @a mime matches the @a accept_pattern - */ -static bool -mime_matches (const char *accept_pattern, - const char *mime) -{ - const char *da = strchr (accept_pattern, '/'); - const char *dm = strchr (mime, '/'); - const char *end; - - if ( (NULL == da) || - (NULL == dm) ) - return (0 == strcmp ("*", accept_pattern)); - /* FIXME: eventually, we might want to parse the "q=$FLOAT" - part after the ';' and figure out which one is the - best/preferred match instead of returning a boolean... */ - end = strchr (da, ';'); - if (NULL == end) - end = &da[strlen (da)]; - return - ( ( (1 == da - accept_pattern) && - ('*' == *accept_pattern) ) || - ( (da - accept_pattern == dm - mime) && - (0 == strncasecmp (accept_pattern, - mime, - da - accept_pattern)) ) ) && - ( (0 == strcmp (da, "/*")) || - (0 == strncasecmp (da, - dm, - end - da)) ); -} - - -bool -TALER_MHD_xmime_matches (const char *accept_pattern, - const char *mime) -{ - char *ap = GNUNET_strdup (accept_pattern); - char *sptr; - - for (const char *tok = strtok_r (ap, ",", &sptr); - NULL != tok; - tok = strtok_r (NULL, ",", &sptr)) - { - if (mime_matches (tok, - mime)) - { - GNUNET_free (ap); - return true; - } - } - GNUNET_free (ap); - return false; -} - - -MHD_RESULT -TALER_MHD_reply_legal (struct MHD_Connection *conn, - struct TALER_MHD_Legal *legal) -{ - struct MHD_Response *resp; - struct Terms *t; - struct GNUNET_TIME_Absolute a; - struct GNUNET_TIME_Timestamp m; - char dat[128]; - char *langs; - - a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING); - m = GNUNET_TIME_absolute_to_timestamp (a); - TALER_MHD_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) - { - const char *etag; - - etag = MHD_lookup_connection_value (conn, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_IF_NONE_MATCH); - if ( (NULL != etag) && - (NULL != legal->terms_etag) && - (0 == strcasecmp (etag, - legal->terms_etag)) ) - { - MHD_RESULT ret; - - resp = MHD_create_response_from_buffer (0, - NULL, - MHD_RESPMEM_PERSISTENT); - TALER_MHD_add_global_headers (resp); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_EXPIRES, - dat)); - - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_ETAG, - legal->terms_etag)); - ret = MHD_queue_response (conn, - MHD_HTTP_NOT_MODIFIED, - resp); - GNUNET_break (MHD_YES == ret); - MHD_destroy_response (resp); - return ret; - } - } - - t = NULL; - langs = NULL; - if (NULL != legal) - { - const char *mime; - const char *lang; - - mime = MHD_lookup_connection_value (conn, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_ACCEPT); - if (NULL == mime) - mime = "text/plain"; - lang = MHD_lookup_connection_value (conn, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_ACCEPT_LANGUAGE); - if (NULL == lang) - lang = "en"; - /* 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) - { - if ( (NULL == t) || - (TALER_MHD_xmime_matches (mime, - p->mime_type)) ) - { - if (NULL == langs) - { - langs = GNUNET_strdup (p->language); - } - else - { - char *tmp = langs; - - GNUNET_asprintf (&langs, - "%s,%s", - tmp, - p->language); - GNUNET_free (tmp); - } - if ( (NULL == t) || - (! TALER_MHD_xmime_matches (mime, - t->mime_type)) || - (TALER_language_matches (lang, - p->language) > - TALER_language_matches (lang, - t->language) ) ) - 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) - { - /* Default terms of service if none are configured */ - static struct Terms none = { - .mime_type = "text/plain", - .terms = "not configured", - .language = "en", - .terms_size = strlen ("not configured") - }; - - t = &none; - } - - /* try to compress the response */ - resp = NULL; - if (MHD_YES == - TALER_MHD_can_compress (conn)) - { - resp = MHD_create_response_from_buffer (t->compressed_terms_size, - t->compressed_terms, - MHD_RESPMEM_PERSISTENT); - if (MHD_NO == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) - { - GNUNET_break (0); - MHD_destroy_response (resp); - resp = NULL; - } - } - if (NULL == resp) - { - /* could not generate compressed response, return uncompressed */ - resp = MHD_create_response_from_buffer (t->terms_size, - (void *) t->terms, - MHD_RESPMEM_PERSISTENT); - } - TALER_MHD_add_global_headers (resp); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_EXPIRES, - dat)); - if (NULL != langs) - { - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - "Avail-Languages", - langs)); - GNUNET_free (langs); - } - /* Set cache control headers: our response varies depending on these headers */ - GNUNET_break (MHD_YES == - MHD_add_response_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_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_CACHE_CONTROL, - "public max-age=864000")); - if (NULL != legal) - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_ETAG, - legal->terms_etag)); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - MHD_HTTP_HEADER_CONTENT_TYPE, - t->mime_type)); - { - MHD_RESULT ret; - - ret = MHD_queue_response (conn, - MHD_HTTP_OK, - resp); - MHD_destroy_response (resp); - return ret; - } -} - - -/** - * 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_MHD_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_etag) || - (0 != strncmp (legal->terms_etag, - name, - ext - name - 1)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Filename `%s' does not match Etag `%s' in directory `%s/%s'. Ignoring it.\n", - name, - legal->terms_etag, - 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; - - t = GNUNET_new (struct Terms); - t->mime_type = mime; - t->terms = buf; - t->language = GNUNET_strdup (lang); - t->terms_size = bsize; - t->priority = priority; - buf = GNUNET_memdup (t->terms, - t->terms_size); - if (TALER_MHD_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_MHD_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_MHD_Legal * -TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg, - const char *section, - const char *diroption, - const char *tagoption) -{ - struct TALER_MHD_Legal *legal; - char *path; - DIR *d; - - legal = GNUNET_new (struct TALER_MHD_Legal); - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - tagoption, - &legal->terms_etag)) - { - 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_etag); - 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_etag); - 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_MHD_legal_free (struct TALER_MHD_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); - } - GNUNET_free (legal->terms_etag); - GNUNET_free (legal); -} diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c @@ -1,444 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014--2020 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 mhd_parsing.c - * @brief functions to parse incoming requests (MHD arguments and JSON snippets) - * @author Florian Dold - * @author Benedikt Mueller - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include "taler_json_lib.h" -#include "taler_mhd_lib.h" - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_post_json (struct MHD_Connection *connection, - void **con_cls, - const char *upload_data, - size_t *upload_data_size, - json_t **json) -{ - enum GNUNET_JSON_PostResult pr; - - pr = GNUNET_JSON_post_parser (TALER_MHD_REQUEST_BUFFER_MAX, - connection, - con_cls, - upload_data, - upload_data_size, - json); - switch (pr) - { - case GNUNET_JSON_PR_OUT_OF_MEMORY: - GNUNET_break (NULL == *json); - return (MHD_NO == - TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_PARSER_OUT_OF_MEMORY, - NULL)) ? GNUNET_SYSERR : GNUNET_NO; - - case GNUNET_JSON_PR_CONTINUE: - GNUNET_break (NULL == *json); - return GNUNET_YES; - case GNUNET_JSON_PR_REQUEST_TOO_LARGE: - GNUNET_break (NULL == *json); - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Closing connection, upload too large\n"); - return GNUNET_SYSERR; - case GNUNET_JSON_PR_JSON_INVALID: - GNUNET_break (NULL == *json); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_JSON_INVALID, - NULL)) - ? GNUNET_NO : GNUNET_SYSERR; - case GNUNET_JSON_PR_SUCCESS: - GNUNET_break (NULL != *json); - return GNUNET_YES; - } - /* this should never happen */ - GNUNET_break (0); - return GNUNET_SYSERR; -} - - -void -TALER_MHD_parse_post_cleanup_callback (void *con_cls) -{ - GNUNET_JSON_post_parser_cleanup (con_cls); -} - - -/** - * Extract fixed-size base32crockford encoded data from request. - * - * Queues an error response to the connection if the parameter is missing or - * invalid. - * - * @param connection the MHD connection - * @param param_name the name of the HTTP key with the value - * @param kind whether to extract from header, argument or footer - * @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 absent or malformed - * #GNUNET_SYSERR on internal error (error response could not be sent) - */ -static enum GNUNET_GenericReturnValue -parse_request_data (struct MHD_Connection *connection, - const char *param_name, - enum MHD_ValueKind kind, - void *out_data, - size_t out_size, - bool *present) -{ - const char *str; - - str = MHD_lookup_connection_value (connection, - kind, - param_name); - if (NULL == str) - { - *present = false; - return GNUNET_OK; - } - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data (str, - strlen (str), - out_data, - out_size)) - return (MHD_NO == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - param_name)) - ? GNUNET_SYSERR : GNUNET_NO; - *present = true; - return GNUNET_OK; -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection, - const char *param_name, - void *out_data, - size_t out_size, - bool *present) -{ - return parse_request_data (connection, - param_name, - MHD_GET_ARGUMENT_KIND, - out_data, - out_size, - present); -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_request_header_data (struct MHD_Connection *connection, - const char *header_name, - void *out_data, - size_t out_size, - bool *present) -{ - return parse_request_data (connection, - header_name, - MHD_HEADER_KIND, - out_data, - out_size, - present); -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection, - struct GNUNET_TIME_Absolute *expiration) -{ - const char *ts; - char dummy; - unsigned long long tms; - - ts = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "timeout_ms"); - if (NULL == ts) - { - *expiration = GNUNET_TIME_UNIT_ZERO_ABS; - return GNUNET_OK; - } - if (1 != - sscanf (ts, - "%llu%c", - &tms, - &dummy)) - { - MHD_RESULT mret; - - GNUNET_break_op (0); - mret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "timeout_ms"); - return (MHD_YES == mret) - ? GNUNET_NO - : GNUNET_SYSERR; - } - *expiration = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, - tms)); - return GNUNET_OK; -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection, - const char *name, - uint64_t *off) -{ - const char *ts; - char dummy; - unsigned long long num; - - ts = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - name); - if (NULL == ts) - return GNUNET_OK; - if (1 != - sscanf (ts, - "%llu%c", - &num, - &dummy)) - { - MHD_RESULT mret; - - GNUNET_break_op (0); - mret = TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - name); - return (MHD_YES == mret) - ? GNUNET_NO - : GNUNET_SYSERR; - } - *off = (uint64_t) num; - return GNUNET_OK; -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_json_data (struct MHD_Connection *connection, - const json_t *root, - struct GNUNET_JSON_Specification *spec) -{ - enum GNUNET_GenericReturnValue ret; - const char *error_json_name; - unsigned int error_line; - - ret = GNUNET_JSON_parse (root, - spec, - &error_json_name, - &error_line); - if (GNUNET_SYSERR == ret) - { - if (NULL == error_json_name) - error_json_name = "<no field>"; - ret = (MHD_YES == - TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_BAD_REQUEST, - GNUNET_JSON_pack_string ("hint", - TALER_ErrorCode_get_hint ( - TALER_EC_GENERIC_JSON_INVALID)), - GNUNET_JSON_pack_uint64 ("code", - TALER_EC_GENERIC_JSON_INVALID), - GNUNET_JSON_pack_string ("field", - error_json_name), - GNUNET_JSON_pack_uint64 ("line", - error_line))) - ? GNUNET_NO : GNUNET_SYSERR; - return ret; - } - return GNUNET_YES; -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_internal_json_data (struct MHD_Connection *connection, - const json_t *root, - struct GNUNET_JSON_Specification *spec) -{ - enum GNUNET_GenericReturnValue ret; - const char *error_json_name; - unsigned int error_line; - - ret = GNUNET_JSON_parse (root, - spec, - &error_json_name, - &error_line); - if (GNUNET_SYSERR == ret) - { - if (NULL == error_json_name) - error_json_name = "<no field>"; - ret = (MHD_YES == - TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_INTERNAL_SERVER_ERROR, - GNUNET_JSON_pack_string ("hint", - TALER_ErrorCode_get_hint ( - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE)), - GNUNET_JSON_pack_uint64 ("code", - TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE), - GNUNET_JSON_pack_string ("field", - error_json_name), - GNUNET_JSON_pack_uint64 ("line", - error_line))) - ? GNUNET_NO : GNUNET_SYSERR; - return ret; - } - return GNUNET_YES; -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_parse_json_array (struct MHD_Connection *connection, - const json_t *root, - struct GNUNET_JSON_Specification *spec, - ...) -{ - enum GNUNET_GenericReturnValue ret; - const char *error_json_name; - unsigned int error_line; - va_list ap; - json_int_t dim; - - va_start (ap, spec); - dim = 0; - while ( (-1 != (ret = va_arg (ap, int))) && - (NULL != root) ) - { - dim++; - root = json_array_get (root, ret); - } - va_end (ap); - if (NULL == root) - { - ret = (MHD_YES == - TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_BAD_REQUEST, - GNUNET_JSON_pack_string ("hint", - TALER_ErrorCode_get_hint ( - TALER_EC_GENERIC_JSON_INVALID)), - GNUNET_JSON_pack_uint64 ("code", - TALER_EC_GENERIC_JSON_INVALID), - GNUNET_JSON_pack_string ("detail", - "expected array"), - GNUNET_JSON_pack_uint64 ("dimension", - dim))) - ? GNUNET_NO : GNUNET_SYSERR; - return ret; - } - ret = GNUNET_JSON_parse (root, - spec, - &error_json_name, - &error_line); - if (GNUNET_SYSERR == ret) - { - if (NULL == error_json_name) - error_json_name = "<no field>"; - ret = (MHD_YES == - TALER_MHD_REPLY_JSON_PACK ( - connection, - MHD_HTTP_BAD_REQUEST, - GNUNET_JSON_pack_string ("detail", - error_json_name), - GNUNET_JSON_pack_string ("hint", - TALER_ErrorCode_get_hint ( - TALER_EC_GENERIC_JSON_INVALID)), - GNUNET_JSON_pack_uint64 ("code", - TALER_EC_GENERIC_JSON_INVALID), - GNUNET_JSON_pack_uint64 ("line", - error_line))) - ? GNUNET_NO : GNUNET_SYSERR; - return ret; - } - return GNUNET_YES; -} - - -enum GNUNET_GenericReturnValue -TALER_MHD_check_content_length_ (struct MHD_Connection *connection, - unsigned long long max_len) -{ - const char *cl; - unsigned long long cv; - char dummy; - - /* Maybe check for maximum upload size - and refuse requests if they are just too big. */ - cl = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_CONTENT_LENGTH); - if (NULL == cl) - { - return GNUNET_OK; -#if 0 - /* wallet currently doesn't always send content-length! */ - GNUNET_break_op (0); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MISSING, - MHD_HTTP_HEADER_CONTENT_LENGTH)) - ? GNUNET_NO - : GNUNET_SYSERR; -#endif - } - if (1 != sscanf (cl, - "%llu%c", - &cv, - &dummy)) - { - /* Not valid HTTP request, just close connection. */ - GNUNET_break_op (0); - return (MHD_YES == - TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - MHD_HTTP_HEADER_CONTENT_LENGTH)) - ? GNUNET_NO - : GNUNET_SYSERR; - } - if (cv > TALER_MHD_REQUEST_BUFFER_MAX) - { - GNUNET_break_op (0); - return (MHD_YES == - TALER_MHD_reply_request_too_large (connection)) - ? GNUNET_NO - : GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/* end of mhd_parsing.c */ diff --git a/src/mhd/mhd_responses.c b/src/mhd/mhd_responses.c @@ -1,550 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2014-2021 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 mhd_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_util.h" -#include "taler_mhd_lib.h" - - -/** - * Global options for response generation. - */ -static enum TALER_MHD_GlobalOptions TM_go; - - -void -TALER_MHD_setup (enum TALER_MHD_GlobalOptions go) -{ - TM_go = go; -} - - -void -TALER_MHD_add_global_headers (struct MHD_Response *response) -{ - if (0 != (TM_go & TALER_MHD_GO_FORCE_CONNECTION_CLOSE)) - GNUNET_break (MHD_YES == - MHD_add_response_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_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_ACCESS_CONTROL_ALLOW_ORIGIN, - "*")); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - /* Not available as MHD constant yet */ - "Access-Control-Expose-Headers", - "*")); -} - - -MHD_RESULT -TALER_MHD_can_compress (struct MHD_Connection *connection) -{ - const char *ae; - const char *de; - - if (0 != (TM_go & TALER_MHD_GO_DISABLE_COMPRESSION)) - return MHD_NO; - ae = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_ACCEPT_ENCODING); - if (NULL == ae) - return MHD_NO; - if (0 == strcmp (ae, - "*")) - return MHD_YES; - de = strstr (ae, - "deflate"); - if (NULL == de) - return MHD_NO; - if ( ( (de == ae) || - (de[-1] == ',') || - (de[-1] == ' ') ) && - ( (de[strlen ("deflate")] == '\0') || - (de[strlen ("deflate")] == ',') || - (de[strlen ("deflate")] == ';') ) ) - return MHD_YES; - return MHD_NO; -} - - -MHD_RESULT -TALER_MHD_body_compress (void **buf, - size_t *buf_size) -{ - Bytef *cbuf; - uLongf cbuf_size; - MHD_RESULT ret; - - cbuf_size = compressBound (*buf_size); - cbuf = malloc (cbuf_size); - if (NULL == cbuf) - return MHD_NO; - ret = compress (cbuf, - &cbuf_size, - (const Bytef *) *buf, - *buf_size); - if ( (Z_OK != ret) || - (cbuf_size >= *buf_size) ) - { - /* compression failed */ - free (cbuf); - return MHD_NO; - } - free (*buf); - *buf = (void *) cbuf; - *buf_size = (size_t) cbuf_size; - return MHD_YES; -} - - -struct MHD_Response * -TALER_MHD_make_json (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_create_response_from_buffer (strlen (json_str), - json_str, - MHD_RESPMEM_MUST_FREE); - if (NULL == response) - { - free (json_str); - GNUNET_break (0); - return NULL; - } - TALER_MHD_add_global_headers (response); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "application/json")); - return response; -} - - -struct MHD_Response * -TALER_MHD_make_json_steal (json_t *json) -{ - struct MHD_Response *res; - - res = TALER_MHD_make_json (json); - json_decref (json); - return res; -} - - -MHD_RESULT -TALER_MHD_reply_json (struct MHD_Connection *connection, - const json_t *json, - unsigned int response_code) -{ - struct MHD_Response *response; - void *json_str; - size_t json_len; - MHD_RESULT 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", - response_code); - - GNUNET_assert (0); - return MHD_NO; - } - json_len = strlen (json_str); - /* try to compress the body */ - is_compressed = MHD_NO; - if (MHD_YES == - TALER_MHD_can_compress (connection)) - is_compressed = TALER_MHD_body_compress (&json_str, - &json_len); - response = MHD_create_response_from_buffer (json_len, - json_str, - MHD_RESPMEM_MUST_FREE); - if (NULL == response) - { - free (json_str); - GNUNET_break (0); - return MHD_NO; - } - TALER_MHD_add_global_headers (response); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "application/json")); - if (MHD_YES == is_compressed) - { - /* Need to indicate to client that body is compressed */ - if (MHD_NO == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_ENCODING, - "deflate")) - { - GNUNET_break (0); - MHD_destroy_response (response); - return MHD_NO; - } - } - - { - MHD_RESULT ret; - - ret = MHD_queue_response (connection, - response_code, - response); - MHD_destroy_response (response); - return ret; - } -} - - -MHD_RESULT -TALER_MHD_reply_json_steal (struct MHD_Connection *connection, - json_t *json, - unsigned int response_code) -{ - MHD_RESULT ret; - - ret = TALER_MHD_reply_json (connection, - json, - response_code); - json_decref (json); - return ret; -} - - -MHD_RESULT -TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection) -{ - struct MHD_Response *response; - - response = MHD_create_response_from_buffer (0, - NULL, - MHD_RESPMEM_PERSISTENT); - if (NULL == response) - return MHD_NO; - /* This adds the Access-Control-Allow-Origin header. - * All endpoints of the exchange allow CORS. */ - TALER_MHD_add_global_headers (response); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - /* Not available as MHD constant yet */ - "Access-Control-Allow-Headers", - "*")); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - /* Not available as MHD constant yet */ - "Access-Control-Allow-Methods", - "*")); - { - MHD_RESULT ret; - - ret = MHD_queue_response (connection, - MHD_HTTP_NO_CONTENT, - response); - MHD_destroy_response (response); - return ret; - } -} - - -MHD_RESULT -TALER_MHD_reply_json_pack (struct MHD_Connection *connection, - unsigned int response_code, - const char *fmt, - ...) -{ - json_t *json; - json_error_t jerror; - - { - va_list argp; - - va_start (argp, - fmt); - json = json_vpack_ex (&jerror, - 0, - fmt, - argp); - va_end (argp); - } - - if (NULL == json) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to pack JSON with format `%s': %s\n", - fmt, - jerror.text); - GNUNET_break (0); - return MHD_NO; - } - - { - MHD_RESULT ret; - - ret = TALER_MHD_reply_json (connection, - json, - response_code); - json_decref (json); - return ret; - } -} - - -struct MHD_Response * -TALER_MHD_make_json_pack (const char *fmt, - ...) -{ - json_t *json; - json_error_t jerror; - - { - va_list argp; - - va_start (argp, fmt); - json = json_vpack_ex (&jerror, - 0, - fmt, - argp); - va_end (argp); - } - - if (NULL == json) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to pack JSON with format `%s': %s\n", - fmt, - jerror.text); - GNUNET_break (0); - return NULL; - } - - { - struct MHD_Response *response; - - response = TALER_MHD_make_json (json); - json_decref (json); - return response; - } -} - - -struct MHD_Response * -TALER_MHD_make_error (enum TALER_ErrorCode ec, - const char *detail) -{ - return TALER_MHD_MAKE_JSON_PACK ( - TALER_MHD_PACK_EC (ec), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("detail", detail))); -} - - -MHD_RESULT -TALER_MHD_reply_with_error (struct MHD_Connection *connection, - unsigned int http_status, - enum TALER_ErrorCode ec, - const char *detail) -{ - return TALER_MHD_REPLY_JSON_PACK ( - connection, - http_status, - TALER_MHD_PACK_EC (ec), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_string ("detail", detail))); -} - - -MHD_RESULT -TALER_MHD_reply_with_ec (struct MHD_Connection *connection, - 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 %d provided for response!\n", - (int) ec); - hc = MHD_HTTP_INTERNAL_SERVER_ERROR; - } - return TALER_MHD_reply_with_error (connection, - hc, - ec, - detail); -} - - -MHD_RESULT -TALER_MHD_reply_request_too_large (struct MHD_Connection *connection) -{ - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_REQUEST_ENTITY_TOO_LARGE, - TALER_EC_GENERIC_UPLOAD_EXCEEDS_LIMIT, - NULL); -} - - -MHD_RESULT -TALER_MHD_reply_agpl (struct MHD_Connection *connection, - 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_create_response_from_buffer (strlen (agpl), - (void *) agpl, - MHD_RESPMEM_PERSISTENT); - if (NULL == response) - { - GNUNET_break (0); - return MHD_NO; - } - TALER_MHD_add_global_headers (response); - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - "text/plain")); - if (MHD_NO == - MHD_add_response_header (response, - MHD_HTTP_HEADER_LOCATION, - url)) - { - GNUNET_break (0); - MHD_destroy_response (response); - return MHD_NO; - } - - { - MHD_RESULT ret; - - ret = MHD_queue_response (connection, - MHD_HTTP_FOUND, - response); - MHD_destroy_response (response); - return ret; - } -} - - -MHD_RESULT -TALER_MHD_reply_static (struct MHD_Connection *connection, - unsigned int http_status, - const char *mime_type, - const char *body, - size_t body_size) -{ - struct MHD_Response *response; - - response = MHD_create_response_from_buffer (body_size, - (void *) body, - MHD_RESPMEM_PERSISTENT); - if (NULL == response) - { - GNUNET_break (0); - return MHD_NO; - } - TALER_MHD_add_global_headers (response); - if (NULL != mime_type) - GNUNET_break (MHD_YES == - MHD_add_response_header (response, - MHD_HTTP_HEADER_CONTENT_TYPE, - mime_type)); - { - MHD_RESULT ret; - - ret = MHD_queue_response (connection, - http_status, - response); - MHD_destroy_response (response); - return ret; - } -} - - -void -TALER_MHD_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/mhd_run.c b/src/mhd/mhd_run.c @@ -1,175 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2019-2021 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 mhd_run.c - * @brief API for running an MHD daemon with the - * GNUnet scheduler - * @author Christian Grothoff - */ -#include "platform.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_json_lib.h> -#include <jansson.h> -#include <microhttpd.h> -#include "taler_util.h" -#include "taler_mhd_lib.h" - - -/** - * Set to true if we should immediately MHD_run() again. - */ -static bool triggered; - -/** - * Task running the HTTP server. - */ -static struct GNUNET_SCHEDULER_Task *mhd_task; - -/** - * The MHD daemon we are running. - */ -static struct MHD_Daemon *mhd; - - -/** - * Function that queries MHD's select sets and - * starts the task waiting for them. - */ -static struct GNUNET_SCHEDULER_Task * -prepare_daemon (void); - - -/** - * Call MHD to process pending requests and then go back - * and schedule the next run. - * - * @param cls NULL - */ -static void -run_daemon (void *cls) -{ - (void) cls; - mhd_task = NULL; - do { - triggered = false; - GNUNET_assert (MHD_YES == - MHD_run (mhd)); - } while (triggered); - mhd_task = prepare_daemon (); -} - - -/** - * Function that queries MHD's select sets and starts the task waiting for - * them. - * - * @return task handle for the MHD task. - */ -static struct GNUNET_SCHEDULER_Task * -prepare_daemon (void) -{ - struct GNUNET_SCHEDULER_Task *ret; - fd_set rs; - fd_set ws; - fd_set es; - struct GNUNET_NETWORK_FDSet *wrs; - struct GNUNET_NETWORK_FDSet *wws; - int max; - MHD_UNSIGNED_LONG_LONG timeout; - int haveto; - struct GNUNET_TIME_Relative tv; - - FD_ZERO (&rs); - FD_ZERO (&ws); - FD_ZERO (&es); - wrs = GNUNET_NETWORK_fdset_create (); - wws = GNUNET_NETWORK_fdset_create (); - max = -1; - GNUNET_assert (MHD_YES == - MHD_get_fdset (mhd, - &rs, - &ws, - &es, - &max)); - haveto = MHD_get_timeout (mhd, - &timeout); - if (haveto == MHD_YES) - tv = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, - timeout); - else - tv = GNUNET_TIME_UNIT_FOREVER_REL; - GNUNET_NETWORK_fdset_copy_native (wrs, - &rs, - max + 1); - GNUNET_NETWORK_fdset_copy_native (wws, - &ws, - max + 1); - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Adding run_daemon select task\n"); - ret = GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_HIGH, - tv, - wrs, - wws, - &run_daemon, - NULL); - GNUNET_NETWORK_fdset_destroy (wrs); - GNUNET_NETWORK_fdset_destroy (wws); - return ret; -} - - -void -TALER_MHD_daemon_start (struct MHD_Daemon *daemon) -{ - GNUNET_assert (NULL == mhd); - mhd = daemon; - mhd_task = prepare_daemon (); -} - - -struct MHD_Daemon * -TALER_MHD_daemon_stop (void) -{ - struct MHD_Daemon *ret; - - if (NULL != mhd_task) - { - GNUNET_SCHEDULER_cancel (mhd_task); - mhd_task = NULL; - } - ret = mhd; - mhd = NULL; - return ret; -} - - -void -TALER_MHD_daemon_trigger (void) -{ - if (NULL != mhd_task) - { - GNUNET_SCHEDULER_cancel (mhd_task); - mhd_task = GNUNET_SCHEDULER_add_now (&run_daemon, - NULL); - } - else - { - triggered = true; - } -} - - -/* end of mhd_run.c */ diff --git a/src/sq/Makefile.am b/src/sq/Makefile.am @@ -1,40 +0,0 @@ -# This Makefile.am is in the public domain -AM_CPPFLAGS = -I$(top_srcdir)/src/include $(LIBGCRYPT_CFLAGS) $(SQLITE_CPPFLAGS) - -if USE_COVERAGE - AM_CFLAGS = --coverage -O0 - XLIB = -lgcov -endif - -lib_LTLIBRARIES = \ - libtalersq.la - -libtalersq_la_SOURCES = \ - sq_query_helper.c \ - sq_result_helper.c -libtalersq_la_LIBADD = \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetutil -ljansson \ - -lsqlite3 \ - $(XLIB) -libtalersq_la_LDFLAGS = \ - $(SQLITE_LDFLAGS) \ - -version-info 0:0:0 \ - -no-undefined - -check_PROGRAMS= \ - test_sq - -TESTS = \ - $(check_PROGRAMS) - -test_sq_SOURCES = \ - test_sq.c -test_sq_LDADD = \ - libtalersq.la \ - $(top_builddir)/src/util/libtalerutil.la \ - -lgnunetsq \ - -lgnunetutil \ - -ljansson \ - -lsqlite3 \ - $(XLIB) diff --git a/src/sq/sq_query_helper.c b/src/sq/sq_query_helper.c @@ -1,175 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 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 sq/sq_query_helper.c - * @brief helper functions for Taler-specific SQLite3 interactions - * @author Jonathan Buchanan - */ -#include "platform.h" -#include <sqlite3.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_sq_lib.h> -#include "taler_sq_lib.h" - - -/** - * Function called to convert input argument into SQL parameters. - * - * @param cls closure - * @param data pointer to input argument, here a `struct TALER_Amount` - * @param data_len number of bytes in @a data (if applicable) - * @param stmt sqlite statement to parameters for - * @param off offset of the argument to bind in @a stmt, numbered from 1, - * so immediately suitable for passing to `sqlite3_bind`-functions. - * @return #GNUNET_SYSERR on error, #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -qconv_amount (void *cls, - const void *data, - size_t data_len, - sqlite3_stmt *stmt, - unsigned int off) -{ - const struct TALER_Amount *amount = data; - - (void) cls; - GNUNET_assert (sizeof (struct TALER_Amount) == data_len); - if (SQLITE_OK != sqlite3_bind_int64 (stmt, - (int) off, - (sqlite3_int64) amount->value)) - return GNUNET_SYSERR; - if (SQLITE_OK != sqlite3_bind_int64 (stmt, - (int) off + 1, - (sqlite3_int64) amount->fraction)) - return GNUNET_SYSERR; - return GNUNET_OK; -} - - -struct GNUNET_SQ_QueryParam -TALER_SQ_query_param_amount (const struct TALER_Amount *x) -{ - struct GNUNET_SQ_QueryParam res = { - .conv = &qconv_amount, - .data = x, - .size = sizeof (*x), - .num_params = 2 - }; - - return res; -} - - -/** - * Function called to convert input argument into SQL parameters. - * - * @param cls closure - * @param data pointer to input argument, here a `struct TALER_AmountNBO` - * @param data_len number of bytes in @a data (if applicable) - * @param stmt sqlite statement to parameters for - * @param off offset of the argument to bind in @a stmt, numbered from 1, - * so immediately suitable for passing to `sqlite3_bind`-functions. - * @return #GNUNET_SYSERR on error, #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -qconv_amount_nbo (void *cls, - const void *data, - size_t data_len, - sqlite3_stmt *stmt, - unsigned int off) -{ - const struct TALER_AmountNBO *amount = data; - struct TALER_Amount amount_hbo; - - (void) cls; - (void) data_len; - TALER_amount_ntoh (&amount_hbo, - amount); - return qconv_amount (cls, - &amount_hbo, - sizeof (struct TALER_Amount), - stmt, - off); -} - - -struct GNUNET_SQ_QueryParam -TALER_SQ_query_param_amount_nbo (const struct TALER_AmountNBO *x) -{ - struct GNUNET_SQ_QueryParam res = { - .conv = &qconv_amount_nbo, - .data = x, - .size = sizeof (*x), - .num_params = 2 - }; - - return res; -} - - -/** - * Function called to convert input argument into SQL parameters. - * - * @param cls closure - * @param data pointer to input argument, here a `struct TALER_Amount` - * @param data_len number of bytes in @a data (if applicable) - * @param stmt sqlite statement to parameters for - * @param off offset of the argument to bind in @a stmt, numbered from 1, - * so immediately suitable for passing to `sqlite3_bind`-functions. - * @return #GNUNET_SYSERR on error, #GNUNET_OK on success - */ -static enum GNUNET_GenericReturnValue -qconv_json (void *cls, - const void *data, - size_t data_len, - sqlite3_stmt *stmt, - unsigned int off) -{ - const json_t *json = data; - char *str; - - (void) cls; - (void) data_len; - str = json_dumps (json, JSON_COMPACT); - if (NULL == str) - return GNUNET_SYSERR; - - if (SQLITE_OK != sqlite3_bind_text (stmt, - (int) off, - str, - strlen (str), - SQLITE_TRANSIENT)) - return GNUNET_SYSERR; - GNUNET_free (str); - return GNUNET_OK; -} - - -struct GNUNET_SQ_QueryParam -TALER_SQ_query_param_json (const json_t *x) -{ - struct GNUNET_SQ_QueryParam res = { - .conv = &qconv_json, - .data = x, - .size = sizeof (*x), - .num_params = 1 - }; - - return res; -} - - -/* end of sq/sq_query_helper.c */ diff --git a/src/sq/sq_result_helper.c b/src/sq/sq_result_helper.c @@ -1,237 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 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 sq/sq_result_helper.c - * @brief functions to initialize parameter arrays - * @author Jonathan Buchanan - */ -#include "platform.h" -#include <sqlite3.h> -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_sq_lib.h> -#include "taler_sq_lib.h" -#include "taler_util.h" - - -/** - * Extract amount data from a SQLite database - * - * @param cls closure, a `const char *` giving the currency - * @param result where to extract data from - * @param column column to extract data from - * @param[in,out] dst_size where to store size of result, may be NULL - * @param[out] dst where to store the result - * @return - * #GNUNET_YES if all results could be extracted - * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) - */ -static enum GNUNET_GenericReturnValue -extract_amount (void *cls, - sqlite3_stmt *result, - unsigned int column, - size_t *dst_size, - void *dst) -{ - struct TALER_Amount *amount = dst; - const char *currency = cls; - if ((sizeof (struct TALER_Amount) != *dst_size) || - (SQLITE_INTEGER != sqlite3_column_type (result, - (int) column)) || - (SQLITE_INTEGER != sqlite3_column_type (result, - (int) column + 1))) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - GNUNET_strlcpy (amount->currency, - currency, - TALER_CURRENCY_LEN); - amount->value = (uint64_t) sqlite3_column_int64 (result, - (int) column); - uint64_t frac = (uint64_t) sqlite3_column_int64 (result, - (int) column + 1); - amount->fraction = (uint32_t) frac; - return GNUNET_YES; -} - - -struct GNUNET_SQ_ResultSpec -TALER_SQ_result_spec_amount (const char *currency, - struct TALER_Amount *amount) -{ - struct GNUNET_SQ_ResultSpec res = { - .conv = &extract_amount, - .cls = (void *) currency, - .dst = (void *) amount, - .dst_size = sizeof (struct TALER_Amount), - .num_params = 2 - }; - - return res; -} - - -/** - * Extract amount data from a SQLite database - * - * @param cls closure, a `const char *` giving the currency - * @param result where to extract data from - * @param column column to extract data from - * @param[in,out] dst_size where to store size of result, may be NULL - * @param[out] dst where to store the result - * @return - * #GNUNET_YES if all results could be extracted - * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) - */ -static enum GNUNET_GenericReturnValue -extract_amount_nbo (void *cls, - sqlite3_stmt *result, - unsigned int column, - size_t *dst_size, - void *dst) -{ - struct TALER_AmountNBO *amount = dst; - struct TALER_Amount amount_hbo; - size_t amount_hbo_size = sizeof (struct TALER_Amount); - - (void) cls; - (void) dst_size; - if (GNUNET_YES != - extract_amount (cls, - result, - column, - &amount_hbo_size, - &amount_hbo)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - TALER_amount_hton (amount, - &amount_hbo); - return GNUNET_YES; -} - - -struct GNUNET_SQ_ResultSpec -TALER_SQ_result_spec_amount_nbo (const char *currency, - struct TALER_AmountNBO *amount) -{ - struct GNUNET_SQ_ResultSpec res = { - .conv = &extract_amount_nbo, - .cls = (void *) currency, - .dst = (void *) amount, - .dst_size = sizeof (struct TALER_AmountNBO), - .num_params = 2 - }; - - return res; -} - - -/** - * Extract amount data from a SQLite database - * - * @param cls closure - * @param result where to extract data from - * @param column column to extract data from - * @param[in,out] dst_size where to store size of result, may be NULL - * @param[out] dst where to store the result - * @return - * #GNUNET_YES if all results could be extracted - * #GNUNET_SYSERR if a result was invalid (non-existing field or NULL) - */ -static enum GNUNET_GenericReturnValue -extract_json (void *cls, - sqlite3_stmt *result, - unsigned int column, - size_t *dst_size, - void *dst) -{ - json_t **j_dst = dst; - const char *res; - json_error_t json_error; - size_t slen; - - (void) cls; - (void) dst_size; - if (SQLITE_TEXT != sqlite3_column_type (result, - column)) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - res = (const char *) sqlite3_column_text (result, - column); - slen = strlen (res); - *j_dst = json_loadb (res, - slen, - JSON_REJECT_DUPLICATES, - &json_error); - if (NULL == *j_dst) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse JSON result for column %d: %s (%s)\n", - column, - json_error.text, - json_error.source); - return GNUNET_SYSERR; - } - return GNUNET_OK; -} - - -/** - * Function called to clean up memory allocated - * by a #GNUNET_SQ_ResultConverter. - * - * @param cls closure - */ -static void -clean_json (void *cls) -{ - json_t **dst = cls; - - (void) cls; - if (NULL != *dst) - { - json_decref (*dst); - *dst = NULL; - } -} - - -/** - * json_t expected. - * - * @param[out] jp where to store the result - * @return array entry for the result specification to use - */ -struct GNUNET_SQ_ResultSpec -TALER_SQ_result_spec_json (json_t **jp) -{ - struct GNUNET_SQ_ResultSpec res = { - .conv = &extract_json, - .cleaner = &clean_json, - .dst = (void *) jp, - .cls = (void *) jp, - .num_params = 1 - }; - - return res; -} - - -/* end of sq/sq_result_helper.c */ diff --git a/src/sq/test_sq.c b/src/sq/test_sq.c @@ -1,215 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2020 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 sq/test_sq.c - * @brief Tests for SQLite3 convenience API - * @author Jonathan Buchanan - */ -#include "platform.h" -#include "taler_sq_lib.h" - - -/** - * Run actual test queries. - * - * @return 0 on success - */ -static int -run_queries (sqlite3 *db) -{ - struct TALER_Amount hamount; - struct TALER_AmountNBO namount; - json_t *json; - sqlite3_stmt *test_insert; - sqlite3_stmt *test_select; - struct GNUNET_SQ_PrepareStatement ps[] = { - GNUNET_SQ_make_prepare ("INSERT INTO test_sq (" - " hamount_val" - ",hamount_frac" - ",namount_val" - ",namount_frac" - ",json" - ") VALUES " - "($1, $2, $3, $4, $5)", - &test_insert), - GNUNET_SQ_make_prepare ("SELECT" - " hamount_val" - ",hamount_frac" - ",namount_val" - ",namount_frac" - ",json" - " FROM test_sq", - &test_select), - GNUNET_SQ_PREPARE_END - }; - int ret = 0; - - GNUNET_assert (GNUNET_OK == - TALER_string_to_amount ("EUR:1.23", - &hamount)); - TALER_amount_hton (&namount, - &hamount); - json = json_object (); - GNUNET_assert (NULL != json); - GNUNET_assert (0 == - json_object_set_new (json, - "foo", - json_integer (42))); - GNUNET_assert (NULL != json); - GNUNET_assert (GNUNET_OK == - GNUNET_SQ_prepare (db, - ps)); - - { - struct GNUNET_SQ_QueryParam params_insert[] = { - TALER_SQ_query_param_amount (&hamount), - TALER_SQ_query_param_amount_nbo (&namount), - TALER_SQ_query_param_json (json), - GNUNET_SQ_query_param_end - }; - GNUNET_SQ_reset (db, - test_insert); - GNUNET_assert (GNUNET_OK == GNUNET_SQ_bind (test_insert, - params_insert)); - GNUNET_assert (SQLITE_DONE == sqlite3_step (test_insert)); - sqlite3_finalize (test_insert); - } - - { - struct TALER_Amount result_amount; - struct TALER_AmountNBO nresult_amount; - struct TALER_Amount nresult_amount_converted; - json_t *result_json; - struct GNUNET_SQ_QueryParam params_select[] = { - GNUNET_SQ_query_param_end - }; - struct GNUNET_SQ_ResultSpec results_select[] = { - TALER_SQ_result_spec_amount ("EUR", - &result_amount), - TALER_SQ_result_spec_amount_nbo ("EUR", - &nresult_amount), - TALER_SQ_result_spec_json (&result_json), - GNUNET_SQ_result_spec_end - }; - - GNUNET_SQ_reset (db, - test_select); - GNUNET_assert (GNUNET_OK == GNUNET_SQ_bind (test_select, - params_select)); - GNUNET_assert (SQLITE_ROW == sqlite3_step (test_select)); - - GNUNET_assert (GNUNET_OK == GNUNET_SQ_extract_result (test_select, - results_select)); - TALER_amount_ntoh (&nresult_amount_converted, - &nresult_amount); - if ((GNUNET_OK != TALER_amount_cmp_currency (&hamount, - &result_amount)) || - (0 != TALER_amount_cmp (&hamount, - &result_amount)) || - (GNUNET_OK != TALER_amount_cmp_currency (&hamount, - &nresult_amount_converted)) || - (0 != TALER_amount_cmp (&hamount, - &nresult_amount_converted)) || - (1 != json_equal (json, - result_json)) ) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Result from database doesn't match input\n"); - ret = 1; - } - GNUNET_SQ_cleanup_result (results_select); - sqlite3_finalize (test_select); - } - json_decref (json); - - return ret; -} - - -int -main (int argc, - const char *const argv[]) -{ - struct GNUNET_SQ_ExecuteStatement es[] = { - GNUNET_SQ_make_execute ("CREATE TEMPORARY TABLE IF NOT EXISTS test_sq (" - " hamount_val INT8 NOT NULL" - ",hamount_frac INT8 NOT NULL" - ",namount_val INT8 NOT NULL" - ",namount_frac INT8 NOT NULL" - ",json VARCHAR NOT NULL" - ")"), - GNUNET_SQ_EXECUTE_STATEMENT_END - }; - sqlite3 *db; - int ret; - - (void) argc; - (void) argv; - GNUNET_log_setup ("test-pq", - "WARNING", - NULL); - - if (SQLITE_OK != sqlite3_open ("talercheck.db", - &db)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to open SQLite3 database\n"); - return 77; - } - - if (GNUNET_OK != GNUNET_SQ_exec_statements (db, - es)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to create new table\n"); - if ((SQLITE_OK != sqlite3_close (db)) || - (0 != unlink ("talercheck.db"))) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to close db or unlink\n"); - } - return 1; - } - - ret = run_queries (db); - - if (SQLITE_OK != - sqlite3_exec (db, - "DROP TABLE test_sq", - NULL, NULL, NULL)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to drop table\n"); - ret = 1; - } - - if (SQLITE_OK != sqlite3_close (db)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to close database\n"); - ret = 1; - } - if (0 != unlink ("talercheck.db")) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to unlink test database file\n"); - ret = 1; - } - return ret; -} - - -/* end of sq/test_sq.c */