summaryrefslogtreecommitdiff
path: root/src/mhd
diff options
context:
space:
mode:
Diffstat (limited to 'src/mhd')
-rw-r--r--src/mhd/Makefile.am12
-rw-r--r--src/mhd/mhd_config.c75
-rw-r--r--src/mhd/mhd_legal.c392
-rw-r--r--src/mhd/mhd_parsing.c524
-rw-r--r--src/mhd/mhd_responses.c258
-rw-r--r--src/mhd/mhd_run.c175
6 files changed, 1014 insertions, 422 deletions
diff --git a/src/mhd/Makefile.am b/src/mhd/Makefile.am
index ec0103f4b..cd3fe7701 100644
--- a/src/mhd/Makefile.am
+++ b/src/mhd/Makefile.am
@@ -13,15 +13,17 @@ libtalermhd_la_SOURCES = \
mhd_config.c \
mhd_legal.c \
mhd_parsing.c \
- mhd_responses.c
+ mhd_responses.c \
+ mhd_run.c
libtalermhd_la_LDFLAGS = \
- -version-info 0:0:0 \
- -export-dynamic -no-undefined
+ -version-info 3:0:3 \
+ -no-undefined
libtalermhd_la_LIBADD = \
- -lgnunetjson \
$(top_builddir)/src/json/libtalerjson.la \
$(top_builddir)/src/util/libtalerutil.la \
+ -lgnunetjson \
-lgnunetutil \
+ -lmicrohttpd \
-ljansson \
+ -lz \
$(XLIB)
-
diff --git a/src/mhd/mhd_config.c b/src/mhd/mhd_config.c
index e2471e9b5..31ec3e476 100644
--- a/src/mhd/mhd_config.c
+++ b/src/mhd/mhd_config.c
@@ -78,7 +78,7 @@ TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
if (GNUNET_OK !=
GNUNET_CONFIGURATION_get_value_number (cfg,
section,
- "port",
+ "PORT",
&port))
{
GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
@@ -124,6 +124,7 @@ TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"unixpath `%s' is too long\n",
*unix_path);
+ GNUNET_free (*unix_path);
return GNUNET_SYSERR;
}
@@ -136,6 +137,7 @@ TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
section,
"UNIXPATH_MODE");
+ GNUNET_free (*unix_path);
return GNUNET_SYSERR;
}
errno = 0;
@@ -147,6 +149,7 @@ TALER_MHD_parse_config (const struct GNUNET_CONFIGURATION_Handle *cfg,
"UNIXPATH_MODE",
"must be octal number");
GNUNET_free (modestring);
+ GNUNET_free (*unix_path);
return GNUNET_SYSERR;
}
GNUNET_free (modestring);
@@ -252,6 +255,7 @@ TALER_MHD_open_unix_path (const char *unix_path,
GNUNET_free (un);
return -1;
}
+
if (GNUNET_OK !=
GNUNET_NETWORK_socket_bind (nh,
(void *) un,
@@ -319,6 +323,53 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
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;
@@ -332,8 +383,14 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
&unixpath_mode))
return -1;
if (NULL != serve_unixpath)
- return TALER_MHD_open_unix_path (serve_unixpath,
- unixpath_mode);
+ {
+ 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,
@@ -390,6 +447,18 @@ TALER_MHD_bind (const struct GNUNET_CONFIGURATION_Handle *cfg,
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,
diff --git a/src/mhd/mhd_legal.c b/src/mhd/mhd_legal.c
index 19a21475f..8353a6901 100644
--- a/src/mhd/mhd_legal.c
+++ b/src/mhd/mhd_legal.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2019 Taler Systems SA
+ Copyright (C) 2019, 2020, 2022, 2024 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
@@ -24,8 +24,14 @@
#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.
@@ -33,12 +39,23 @@
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!).
+ * The terms (NOT 0-terminated!), mmap()'ed. Do not free,
+ * use munmap() instead.
*/
void *terms;
@@ -48,9 +65,27 @@ struct Terms
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;
+
};
@@ -61,14 +96,14 @@ struct Terms
struct TALER_MHD_Legal
{
/**
- * Array of terms of service, terminated by NULL/0 value.
+ * DLL of terms of service.
*/
- struct Terms *terms;
+ struct Terms *terms_head;
/**
- * Length of the #terms array.
+ * DLL of terms of service.
*/
- unsigned int terms_len;
+ struct Terms *terms_tail;
/**
* Etag to use for the terms of service (= version).
@@ -80,7 +115,8 @@ struct TALER_MHD_Legal
/**
* Check if @a mime matches the @a accept_pattern.
*
- * @param accept_pattern a mime pattern like text/plain or image/STAR
+ * @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
*/
@@ -90,10 +126,18 @@ mime_matches (const char *accept_pattern,
{
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: use TALER_MHD_check_accept() here!
+ /* 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) ) ||
@@ -102,72 +146,60 @@ mime_matches (const char *accept_pattern,
mime,
da - accept_pattern)) ) ) &&
( (0 == strcmp (da, "/*")) ||
- (0 == strcasecmp (da,
- dm)) );
+ (0 == strncasecmp (da,
+ dm,
+ end - da)) );
}
-/**
- * Check if @a lang matches the @a language_pattern, and if so with
- * which preference.
- * See also: https://tools.ietf.org/html/rfc7231#section-5.3.1
- *
- * @param language_pattern a language preferences string
- * like "fr-CH, fr;q=0.9, en;q=0.8, *;q=0.1"
- * @param lang the 2-digit language to match
- * @return q-weight given for @a lang in @a language_pattern, 1.0 if no weights are given;
- * 0 if @a lang is not in @a language_pattern
- */
-static double
-language_matches (const char *language_pattern,
- const char *lang)
+bool
+TALER_MHD_xmime_matches (const char *accept_pattern,
+ const char *mime)
{
- char *p = GNUNET_strdup (language_pattern);
+ char *ap = GNUNET_strdup (accept_pattern);
char *sptr;
- double r = 0.0;
- for (char *tok = strtok_r (p, ",", &sptr);
+ for (const char *tok = strtok_r (ap, ",", &sptr);
NULL != tok;
tok = strtok_r (NULL, ",", &sptr))
{
- char *sptr2;
- char *lp = strtok_r (tok, ";", &sptr2);
- char *qp = strtok_r (NULL, ";", &sptr2);
- double q = 1.0;
-
- while (isspace ((int) *lp))
- lp++;
- if (NULL != qp)
- while (isspace ((int) *qp))
- qp++;
- GNUNET_break_op ( (NULL == qp) ||
- (1 == sscanf (qp,
- "q=%lf",
- &q)) );
- if (0 == strcasecmp (lang,
- lp))
- r = GNUNET_MAX (r, q);
+ if (mime_matches (tok,
+ mime))
+ {
+ GNUNET_free (ap);
+ return true;
+ }
}
- GNUNET_free (p);
- return r;
+ GNUNET_free (ap);
+ return false;
}
-/**
- * Generate a response with a legal document in the format and language of the
- * user's choosing.
- *
- * @param conn HTTP connection to handle
- * @param legal legal document to serve
- * @return MHD result code
- */
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);
+ /* 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_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;
@@ -186,6 +218,15 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
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);
@@ -196,6 +237,7 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
}
t = NULL;
+ langs = NULL;
if (NULL != legal)
{
const char *mime;
@@ -205,7 +247,7 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_ACCEPT);
if (NULL == mime)
- mime = "text/html";
+ mime = "text/plain";
lang = MHD_lookup_connection_value (conn,
MHD_HEADER_KIND,
MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
@@ -213,21 +255,36 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
lang = "en";
/* Find best match: must match mime type (if possible), and if
mime type matches, ideally also language */
- for (unsigned int i = 0; i < legal->terms_len; i++)
+ for (struct Terms *p = legal->terms_head;
+ NULL != p;
+ p = p->next)
{
- struct Terms *p = &legal->terms[i];
-
if ( (NULL == t) ||
- (mime_matches (mime,
- p->mime_type)) )
+ (TALER_MHD_xmime_matches (mime,
+ p->mime_type)) )
{
+ 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);
+ }
if ( (NULL == t) ||
- (! mime_matches (mime,
- t->mime_type)) ||
- (language_matches (lang,
- p->language) >
- language_matches (lang,
- t->language) ) )
+ (! TALER_MHD_xmime_matches (mime,
+ t->mime_type)) ||
+ (TALER_language_matches (lang,
+ p->language) >
+ TALER_language_matches (lang,
+ t->language) ) )
t = p;
}
}
@@ -257,29 +314,17 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
if (MHD_YES ==
TALER_MHD_can_compress (conn))
{
- void *buf = GNUNET_memdup (t->terms,
- t->terms_size);
- size_t buf_size = t->terms_size;
-
- if (TALER_MHD_body_compress (&buf,
- &buf_size))
- {
- resp = MHD_create_response_from_buffer (buf_size,
- buf,
- MHD_RESPMEM_MUST_FREE);
- if (MHD_NO ==
- MHD_add_response_header (resp,
- MHD_HTTP_HEADER_CONTENT_ENCODING,
- "deflate"))
- {
- GNUNET_break (0);
- MHD_destroy_response (resp);
- resp = NULL;
- }
- }
- else
+ 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_free (buf);
+ GNUNET_break (0);
+ MHD_destroy_response (resp);
+ resp = NULL;
}
}
if (NULL == resp)
@@ -290,6 +335,30 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
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,
@@ -299,6 +368,10 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
MHD_add_response_header (resp,
MHD_HTTP_HEADER_CONTENT_TYPE,
t->mime_type));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_CONTENT_LANGUAGE,
+ t->language));
{
MHD_RESULT ret;
@@ -330,21 +403,24 @@ load_terms (struct TALER_MHD_Legal *legal,
{
const char *ext;
const char *mime;
+ unsigned int priority;
} mm[] = {
- { .ext = ".html", .mime = "text/html" },
- { .ext = ".htm", .mime = "text/html" },
- { .ext = ".txt", .mime = "text/plain" },
- { .ext = ".pdf", .mime = "application/pdf" },
+ { .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" },
- { .ext = ".xml", .mime = "text/xml" },
+ { .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)
{
@@ -360,7 +436,7 @@ load_terms (struct TALER_MHD_Legal *legal,
name,
ext - name - 1)) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
"Filename `%s' does not match Etag `%s' in directory `%s/%s'. Ignoring it.\n",
name,
legal->terms_etag,
@@ -374,6 +450,7 @@ load_terms (struct TALER_MHD_Legal *legal,
ext))
{
mime = mm[i].mime;
+ priority = mm[i].priority;
break;
}
if (NULL == mime)
@@ -424,50 +501,70 @@ load_terms (struct TALER_MHD_Legal *legal,
GNUNET_free (fn);
return;
}
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Loading legal information from file `%s'\n",
+ fn);
{
- char *buf;
+ void *buf;
size_t bsize;
- ssize_t ret;
bsize = (size_t) st.st_size;
- buf = GNUNET_malloc_large (bsize);
- if (NULL == buf)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
- "malloc");
- GNUNET_break (0 == close (fd));
- GNUNET_free (fn);
- return;
- }
- ret = read (fd,
- buf,
- bsize);
- if ( (ret < 0) ||
- (bsize != ((size_t) ret)) )
+ buf = mmap (NULL,
+ bsize,
+ PROT_READ,
+ MAP_SHARED,
+ fd,
+ 0);
+ if (MAP_FAILED == buf)
{
GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
- "read",
+ "mmap",
fn);
GNUNET_break (0 == close (fd));
- GNUNET_free (buf);
GNUNET_free (fn);
return;
}
GNUNET_break (0 == close (fd));
GNUNET_free (fn);
- /* append to global list of terms of service */
+ /* insert into global list of terms of service */
{
- struct Terms t = {
- .mime_type = mime,
- .terms = buf,
- .language = GNUNET_strdup (lang),
- .terms_size = bsize
- };
-
- GNUNET_array_append (legal->terms,
- legal->terms_len,
- t);
+ 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);
+ }
}
}
}
@@ -507,26 +604,16 @@ load_language (struct TALER_MHD_Legal *legal,
if (fn[0] == '.')
continue;
- load_terms (legal, path, lang, fn);
+ load_terms (legal,
+ path,
+ lang,
+ fn);
}
GNUNET_break (0 == closedir (d));
GNUNET_free (dname);
}
-/**
- * Load set of legal documents as specified in @a cfg in section @a section
- * where the Etag is given under the @param 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_MHD_Legal *
TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
const char *section,
@@ -583,7 +670,12 @@ TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
if (lang[0] == '.')
continue;
- load_language (legal, path, lang);
+ if (0 == strcmp (lang,
+ "locale"))
+ continue;
+ load_language (legal,
+ path,
+ lang);
}
GNUNET_break (0 == closedir (d));
GNUNET_free (path);
@@ -591,26 +683,24 @@ TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
}
-/**
- * Free set of legal documents
- *
- * @param legal legal documents to free
- */
void
TALER_MHD_legal_free (struct TALER_MHD_Legal *legal)
{
+ struct Terms *t;
if (NULL == legal)
return;
- for (unsigned int i = 0; i<legal->terms_len; i++)
+ while (NULL != (t = legal->terms_head))
{
- struct Terms *t = &legal->terms[i];
-
GNUNET_free (t->language);
- GNUNET_free (t->terms);
+ 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_array_grow (legal->terms,
- legal->terms_len,
- 0);
GNUNET_free (legal->terms_etag);
GNUNET_free (legal);
}
diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c
index fca54f3ff..771319bd5 100644
--- a/src/mhd/mhd_parsing.c
+++ b/src/mhd/mhd_parsing.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014--2019 Taler Systems SA
+ Copyright (C) 2014--2024 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
@@ -27,36 +27,7 @@
#include "taler_mhd_lib.h"
-/**
- * Maximum POST request size.
- */
-#define REQUEST_BUFFER_MAX (1024 * 1024)
-
-
-/**
- * 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 @a con_cls, which must be cleaned up using
- * #TALER_MHD_parse_post_cleanup_callback().
- *
- * @param connection the MHD connection
- * @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)
- */
-int
+enum GNUNET_GenericReturnValue
TALER_MHD_parse_post_json (struct MHD_Connection *connection,
void **con_cls,
const char *upload_data,
@@ -65,7 +36,7 @@ TALER_MHD_parse_post_json (struct MHD_Connection *connection,
{
enum GNUNET_JSON_PostResult pr;
- pr = GNUNET_JSON_post_parser (REQUEST_BUFFER_MAX,
+ pr = GNUNET_JSON_post_parser (TALER_MHD_REQUEST_BUFFER_MAX,
connection,
con_cls,
upload_data,
@@ -76,27 +47,27 @@ TALER_MHD_parse_post_json (struct MHD_Connection *connection,
case GNUNET_JSON_PR_OUT_OF_MEMORY:
GNUNET_break (NULL == *json);
return (MHD_NO ==
- TALER_MHD_reply_with_error
- (connection,
+ TALER_MHD_reply_with_error (
+ connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_PARSER_OUT_OF_MEMORY,
- "out of memory")) ? GNUNET_SYSERR : GNUNET_NO;
+ 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);
- return (MHD_NO ==
- TALER_MHD_reply_request_too_large
- (connection)) ? GNUNET_SYSERR : GNUNET_NO;
+ 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_JSON_INVALID,
- "invalid JSON uploaded"))
+ TALER_EC_GENERIC_JSON_INVALID,
+ NULL))
? GNUNET_NO : GNUNET_SYSERR;
case GNUNET_JSON_PR_SUCCESS:
GNUNET_break (NULL != *json);
@@ -108,13 +79,6 @@ TALER_MHD_parse_post_json (struct MHD_Connection *connection,
}
-/**
- * Function called whenever we are done with a request
- * to clean up our state.
- *
- * @param con_cls value as it was left by
- * #TALER_MHD_parse_post_json(), to be cleaned up
- */
void
TALER_MHD_parse_post_cleanup_callback (void *con_cls)
{
@@ -123,39 +87,39 @@ TALER_MHD_parse_post_cleanup_callback (void *con_cls)
/**
- * Extract base32crockford encoded data from request.
+ * Extract fixed-size base32crockford encoded data from request.
*
- * Queues an error response to the connection if the parameter is
- * missing or invalid.
+ * 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 parameter with the key
+ * @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 data
+ * @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)
*/
-int
-TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
- const char *param_name,
- void *out_data,
- size_t out_size)
+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,
- MHD_GET_ARGUMENT_KIND,
+ kind,
param_name);
if (NULL == str)
{
- return (MHD_NO ==
- TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MISSING,
- param_name))
- ? GNUNET_SYSERR : GNUNET_NO;
+ *present = false;
+ return GNUNET_OK;
}
if (GNUNET_OK !=
GNUNET_STRINGS_string_to_data (str,
@@ -165,33 +129,195 @@ TALER_MHD_parse_request_arg_data (struct MHD_Connection *connection,
return (MHD_NO ==
TALER_MHD_reply_with_error (connection,
MHD_HTTP_BAD_REQUEST,
- TALER_EC_PARAMETER_MALFORMED,
+ TALER_EC_GENERIC_PARAMETER_MALFORMED,
param_name))
? GNUNET_SYSERR : GNUNET_NO;
+ *present = true;
return GNUNET_OK;
}
-/**
- * Parse JSON object into components based on the given field
- * specification. Generates error response on parse errors.
- *
- * @param connection the connection 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
- * @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
- */
-int
+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_request_arg_snumber (struct MHD_Connection *connection,
+ const char *name,
+ int64_t *val)
+{
+ const char *ts;
+ char dummy;
+ long long num;
+
+ ts = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ name);
+ if (NULL == ts)
+ return GNUNET_OK;
+ if (1 !=
+ sscanf (ts,
+ "%lld%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;
+ }
+ *val = (int64_t) num;
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
+TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection,
+ const char *name,
+ struct TALER_Amount *val)
+{
+ const char *ts;
+
+ ts = MHD_lookup_connection_value (connection,
+ MHD_GET_ARGUMENT_KIND,
+ name);
+ if (NULL == ts)
+ return GNUNET_OK;
+ if (GNUNET_OK !=
+ TALER_string_to_amount (ts,
+ val))
+ {
+ 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;
+ }
+ return GNUNET_OK;
+}
+
+
+enum GNUNET_GenericReturnValue
TALER_MHD_parse_json_data (struct MHD_Connection *connection,
const json_t *root,
struct GNUNET_JSON_Specification *spec)
{
- int ret;
+ enum GNUNET_GenericReturnValue ret;
const char *error_json_name;
unsigned int error_line;
@@ -204,15 +330,18 @@ TALER_MHD_parse_json_data (struct MHD_Connection *connection,
if (NULL == error_json_name)
error_json_name = "<no field>";
ret = (MHD_YES ==
- TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s, s:I, s:s, s:I}",
- "hint", "JSON parse error",
- "code",
- (json_int_t)
- TALER_EC_JSON_INVALID_WITH_DETAILS,
- "field", error_json_name,
- "line", (json_int_t) error_line))
+ 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;
}
@@ -220,28 +349,50 @@ TALER_MHD_parse_json_data (struct MHD_Connection *connection,
}
-/**
- * Parse JSON array into components based on the given field
- * specification. Generates error response on parse errors.
- *
- * @param connection the connection 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
- */
-int
+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,
...)
{
- int ret;
+ enum GNUNET_GenericReturnValue ret;
const char *error_json_name;
unsigned int error_line;
va_list ap;
@@ -259,14 +410,18 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
if (NULL == root)
{
ret = (MHD_YES ==
- TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s, s:I, s:I}",
- "hint", "expected array",
- "code",
- (json_int_t)
- TALER_EC_JSON_INVALID_WITH_DETAILS,
- "dimension", dim))
+ 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;
}
@@ -279,14 +434,18 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
if (NULL == error_json_name)
error_json_name = "<no field>";
ret = (MHD_YES ==
- TALER_MHD_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s, s:I, s:I}",
- "hint", error_json_name,
- "code",
- (json_int_t)
- TALER_EC_JSON_INVALID_WITH_DETAILS,
- "line", (json_int_t) error_line))
+ 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;
}
@@ -294,4 +453,121 @@ TALER_MHD_parse_json_array (struct MHD_Connection *connection,
}
+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;
+}
+
+
+int
+TALER_MHD_check_accept (struct MHD_Connection *connection,
+ const char *header,
+ ...)
+{
+ bool ret = false;
+ const char *accept;
+ char *a;
+ char *saveptr;
+
+ accept = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ header);
+ if (NULL == accept)
+ return -2; /* no Accept header set */
+
+ a = GNUNET_strdup (accept);
+ for (char *t = strtok_r (a, ",", &saveptr);
+ NULL != t;
+ t = strtok_r (NULL, ",", &saveptr))
+ {
+ char *end;
+
+ /* skip leading whitespace */
+ while (isspace ((unsigned char) t[0]))
+ t++;
+ /* trim of ';q=' parameter and everything after space */
+ /* 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 (t, ';');
+ if (NULL != end)
+ *end = '\0';
+ end = strchr (t, ' ');
+ if (NULL != end)
+ *end = '\0';
+ {
+ va_list ap;
+ int off = 0;
+ const char *val;
+
+ va_start (ap,
+ header);
+ while (NULL != (val = va_arg (ap,
+ const char *)))
+ {
+ if (0 == strcasecmp (val,
+ t))
+ {
+ ret = off;
+ break;
+ }
+ off++;
+ }
+ va_end (ap);
+ }
+ }
+ GNUNET_free (a);
+ return ret;
+}
+
+
/* end of mhd_parsing.c */
diff --git a/src/mhd/mhd_responses.c b/src/mhd/mhd_responses.c
index 45a9932d3..7dd6824e2 100644
--- a/src/mhd/mhd_responses.c
+++ b/src/mhd/mhd_responses.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014-2020 Taler Systems SA
+ 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
@@ -32,12 +32,6 @@
static enum TALER_MHD_GlobalOptions TM_go;
-/**
- * Set global options for response generation
- * within libtalermhd.
- *
- * @param go global options to use
- */
void
TALER_MHD_setup (enum TALER_MHD_GlobalOptions go)
{
@@ -45,13 +39,6 @@ TALER_MHD_setup (enum TALER_MHD_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
- */
void
TALER_MHD_add_global_headers (struct MHD_Response *response)
{
@@ -66,20 +53,14 @@ TALER_MHD_add_global_headers (struct MHD_Response *response)
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",
+ "*"));
}
-/**
- * Is HTTP body deflate compression supported by the client?
- *
- * @param connection connection to check
- * @return #MHD_YES if 'deflate' compression is allowed
- *
- * Note that right now we're ignoring q-values, which is technically
- * not correct, and also do not support "*" anywhere but in a line by
- * itself. This should eventually be fixed, see also
- * https://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html
- */
MHD_RESULT
TALER_MHD_can_compress (struct MHD_Connection *connection)
{
@@ -111,13 +92,6 @@ TALER_MHD_can_compress (struct MHD_Connection *connection)
}
-/**
- * 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 #MHD_YES if @a buf was compressed
- */
MHD_RESULT
TALER_MHD_body_compress (void **buf,
size_t *buf_size)
@@ -148,12 +122,6 @@ TALER_MHD_body_compress (void **buf,
}
-/**
- * Make JSON response object.
- *
- * @param json the json object
- * @return MHD response object
- */
struct MHD_Response *
TALER_MHD_make_json (const json_t *json)
{
@@ -185,14 +153,17 @@ TALER_MHD_make_json (const json_t *json)
}
-/**
- * Send JSON object as response.
- *
- * @param connection the MHD connection
- * @param json the json object
- * @param response_code the http response code
- * @return MHD result code
- */
+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,
@@ -264,13 +235,21 @@ TALER_MHD_reply_json (struct MHD_Connection *connection,
}
-/**
- * Send back a "204 No Content" response with headers
- * for the CORS pre-flight request.
- *
- * @param connection the MHD connection
- * @return MHD result code
- */
+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)
{
@@ -289,7 +268,11 @@ TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection)
/* 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;
@@ -302,16 +285,6 @@ TALER_MHD_reply_cors_preflight (struct MHD_Connection *connection)
}
-/**
- * Function to call to handle the request by building a JSON
- * reply from a format string and varargs.
- *
- * @param connection the MHD connection to handle
- * @param response_code HTTP response code to use
- * @param fmt format string for pack
- * @param ... varargs
- * @return MHD result code
- */
MHD_RESULT
TALER_MHD_reply_json_pack (struct MHD_Connection *connection,
unsigned int response_code,
@@ -355,13 +328,6 @@ TALER_MHD_reply_json_pack (struct MHD_Connection *connection,
}
-/**
- * Make JSON response object.
- *
- * @param fmt format string for pack
- * @param ... varargs
- * @return MHD response object
- */
struct MHD_Response *
TALER_MHD_make_json_pack (const char *fmt,
...)
@@ -387,7 +353,7 @@ TALER_MHD_make_json_pack (const char *fmt,
fmt,
jerror.text);
GNUNET_break (0);
- return MHD_NO;
+ return NULL;
}
{
@@ -400,84 +366,64 @@ TALER_MHD_make_json_pack (const char *fmt,
}
-/**
- * Create a response indicating an internal error.
- *
- * @param ec error code to return
- * @param hint hint about the internal error's nature
- * @return a MHD response object
- */
struct MHD_Response *
TALER_MHD_make_error (enum TALER_ErrorCode ec,
- const char *hint)
+ const char *detail)
{
- return TALER_MHD_make_json_pack ("{s:I, s:s}",
- "code", (json_int_t) ec,
- "hint", hint);
+ return TALER_MHD_MAKE_JSON_PACK (
+ TALER_MHD_PACK_EC (ec),
+ GNUNET_JSON_pack_allow_null (
+ GNUNET_JSON_pack_string ("detail", detail)));
}
-/**
- * Send a response indicating an error.
- *
- * @param connection the MHD connection to use
- * @param ec error code uniquely identifying the error
- * @param http_status HTTP status code to use
- * @param hint human readable hint about the error
- * @return a MHD result code
- */
MHD_RESULT
TALER_MHD_reply_with_error (struct MHD_Connection *connection,
unsigned int http_status,
enum TALER_ErrorCode ec,
- const char *hint)
+ const char *detail)
{
- return TALER_MHD_reply_json_pack (connection,
- http_status,
- "{s:I, s:s}",
- "code", (json_int_t) ec,
- "hint", hint);
+ 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)));
}
-/**
- * Send a response indicating that the request was too big.
- *
- * @param connection the MHD connection to use
- * @return a MHD result code
- */
MHD_RESULT
-TALER_MHD_reply_request_too_large (struct MHD_Connection *connection)
+TALER_MHD_reply_with_ec (struct MHD_Connection *connection,
+ enum TALER_ErrorCode ec,
+ const char *detail)
{
- struct MHD_Response *response;
-
- response = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- if (NULL == response)
- return MHD_NO;
- TALER_MHD_add_global_headers (response);
+ unsigned int hc = TALER_ErrorCode_get_http_status (ec);
+ if ( (0 == hc) ||
+ (UINT_MAX == hc) )
{
- MHD_RESULT ret;
-
- ret = MHD_queue_response (connection,
- MHD_HTTP_REQUEST_ENTITY_TOO_LARGE,
- response);
- MHD_destroy_response (response);
- return ret;
+ 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);
}
-/**
- * Function to call to handle the request by sending
- * back a redirect to the AGPL source code.
- *
- * @param connection the MHD connection to handle
- * @param url where to redirect for the sources
- * @return MHD result code
- */
MHD_RESULT
TALER_MHD_reply_agpl (struct MHD_Connection *connection,
const char *url)
@@ -521,17 +467,6 @@ TALER_MHD_reply_agpl (struct MHD_Connection *connection,
}
-/**
- * Function to call to handle the request by sending
- * back static data.
- *
- * @param connection the MHD connection to handle
- * @param http_status 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 result code
- */
MHD_RESULT
TALER_MHD_reply_static (struct MHD_Connection *connection,
unsigned int http_status,
@@ -567,4 +502,49 @@ TALER_MHD_reply_static (struct MHD_Connection *connection,
}
+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
new file mode 100644
index 000000000..8388fbff6
--- /dev/null
+++ b/src/mhd/mhd_run.c
@@ -0,0 +1,175 @@
+/*
+ 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 */