commit 6c993afa87f882acf31de13af4f551b5f8171f0e
parent 54306aeb704d7078f93f4987511dc6f5f64399ed
Author: Christian Grothoff <christian@grothoff.org>
Date: Mon, 20 Apr 2026 15:34:43 +0200
more basics on Paivana
Diffstat:
6 files changed, 478 insertions(+), 382 deletions(-)
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
@@ -12,6 +12,7 @@ bin_PROGRAMS = \
paivana_httpd_SOURCES = \
paivana-httpd.c \
paivana-httpd_cookie.c paivana-httpd_cookie.h \
+ paivana-httpd_daemon.c paivana-httpd_daemon.h \
paivana-httpd_helper.c paivana-httpd_helper.h \
paivana-httpd_reverse.c paivana-httpd_reverse.h \
paivana-httpd_pay.c paivana-httpd_pay.h \
diff --git a/src/backend/paivana-httpd.c b/src/backend/paivana-httpd.c
@@ -1,7 +1,7 @@
/*
This file is part of GNU Taler
Copyright (C) 2012-2014 GNUnet e.V.
- Copyright (C) 2018, 2025 Taler Systems SA
+ Copyright (C) 2018, 2025, 2026 Taler Systems SA
GNU Taler is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
@@ -33,6 +33,7 @@
#include <taler/taler_mhd_lib.h>
#include "paivana-httpd.h"
#include "paivana-httpd_cookie.h"
+#include "paivana-httpd_daemon.h"
#include "paivana-httpd_helper.h"
#include "paivana-httpd_pay.h"
#include "paivana-httpd_reverse.h"
@@ -40,41 +41,6 @@
#include "paivana_pd.h"
-struct RequestContext
-{
-
- /**
- * HTTP connection to the client.
- */
- struct MHD_Connection *connection;
-
- /**
- * Handle for request forwarding as reverse proxy.
- */
- struct HttpRequest *hr;
-
- /**
- * Handle for processing actual payment.
- */
- struct PayRequest *hp;
-
- /**
- * Full request URL.
- */
- char *url;
-
- /**
- * True if this is a POST to the .well-known/paivana endpoint.
- */
- bool is_paivana;
-
- /**
- * We are past the paywall, forward to client.
- */
- bool do_forward;
-};
-
-
char *PH_target_server_base_url;
char *PH_merchant_base_url;
@@ -83,6 +49,9 @@ char *PH_base_url;
struct GNUNET_CURL_Context *PH_ctx;
+int PH_no_check;
+
+int PH_global_ret;
/**
* Closure for #GNUNET_CURL_gnunet_scheduler_reschedule().
@@ -90,296 +59,10 @@ struct GNUNET_CURL_Context *PH_ctx;
static struct GNUNET_CURL_RescheduleContext *ctx_rc;
/**
- * Set to true if we started a daemon.
- */
-static bool have_daemons;
-
-/**
- * Static paywall response.
- */
-static struct MHD_Response *paywall;
-
-/**
* Our configuration.
*/
static const struct GNUNET_CONFIGURATION_Handle *cfg;
-/**
- * Disable paywall check.
- */
-static int no_check;
-
-/**
- * Value to return from main()
- */
-static int global_ret;
-
-
-/**
- * Main MHD callback for handling requests.
- *
- * @param cls unused
- * @param con MHD connection handle
- * @param url the url in the request
- * @param meth the HTTP method used ("GET", "PUT", etc.)
- * @param ver the HTTP version string (i.e. "HTTP/1.1")
- * @param upload_data the data being uploaded (excluding HEADERS,
- * for a POST that fits into memory and that is encoded
- * with a supported encoding, the POST data will NOT be
- * given in upload_data and is instead available as
- * part of MHD_get_connection_values; very large POST
- * data *will* be made available incrementally in
- * upload_data)
- * @param upload_data_size set initially to the size of the
- * @a upload_data provided; the method must update this
- * value to the number of bytes NOT processed;
- * @param con_cls pointer to location where we store the
- * 'struct Request'
- * @return #MHD_YES if the connection was handled successfully,
- * #MHD_NO if the socket must be closed due to a serious
- * error while handling the request
- */
-static enum MHD_Result
-create_response (void *cls,
- struct MHD_Connection *con,
- const char *url,
- const char *meth,
- const char *ver,
- const char *upload_data,
- size_t *upload_data_size,
- void **con_cls)
-{
- struct RequestContext *rc = *con_cls;
- const char *cookie;
-
- (void) cls;
- if ( (! rc->is_paivana) &&
- (0 == strcmp (url,
- ".well-known/paivana")) &&
- (0 == strcasecmp (meth,
- "POST")) )
- {
- rc->is_paivana = true;
- }
- if (rc->is_paivana)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Client POSTed payment, checking validity\n");
- if (NULL == rc->hp)
- rc->hp = PAIVANA_HTTPD_payment_create (rc->connection);
- return PAIVANA_HTTPD_payment_handle (rc->hp,
- upload_data,
- upload_data_size);
- }
- // FIXME: check if url is one that we require payment for,
- // if not set 'do_forward = true'.
- // (also should determine WHICH payment template
- // we use => load *all* templates of the instance,
- // pre-compile *all* regexes and then match!)
- // -> Then needs *restart* on template changes!
- // -> in future: long-poll on GET /private/templates?
-
- if (rc->do_forward)
- {
- if (NULL == rc->hr)
- rc->hr = PAIVANA_HTTPD_reverse_create (rc->connection,
- rc->url);
- return PAIVANA_HTTPD_reverse (rc->hr,
- con,
- url,
- meth,
- ver,
- upload_data,
- upload_data_size);
- }
-
- cookie = MHD_lookup_connection_value (con,
- MHD_COOKIE_KIND,
- "Paivana-Cookie");
- if (NULL != cookie)
- {
- void *ca;
- size_t ca_len;
- struct GNUNET_Buffer buf;
- char *website;
- bool ok;
-
- GNUNET_break (PAIVANA_HTTPD_get_client_address (con,
- &ca,
- &ca_len));
- if (! PAIVANA_HTTPD_get_base_url (con,
- &buf))
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (
- con,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
- "Host or X-Forwarded-Host required");
- }
- GNUNET_buffer_write_str (&buf,
- url);
- website = GNUNET_buffer_reap_str (&buf);
- ok = PAIVANA_HTTPD_check_cookie (cookie,
- website,
- ca_len,
- ca);
- GNUNET_free (website);
- GNUNET_free (ca);
- if (! ok)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request denied\n");
- return MHD_queue_response (con,
- MHD_HTTP_PAYMENT_REQUIRED,
- paywall);
- }
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Request ok!\n");
- rc->do_forward = true;
- /* TODO: hacks for 100 continue suppression should go here! */
- return MHD_YES;
-}
-
-
-/**
- * Function called when MHD decides that we
- * are done with a request.
- *
- * @param cls NULL
- * @param connection connection handle
- * @param con_cls value as set by the last call to
- * the MHD_AccessHandlerCallback, should be
- * our `struct RequestContext *` (created in `mhd_log_callback()`)
- * @param toe reason for request termination (ignored)
- */
-static void
-mhd_completed_cb (void *cls,
- struct MHD_Connection *connection,
- void **con_cls,
- enum MHD_RequestTerminationCode toe)
-{
- struct RequestContext *rc = *con_cls;
-
- (void) cls;
- (void) connection;
- if (NULL == rc)
- return;
- if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "MHD encountered error handling request to %s: %d\n",
- rc->url,
- toe);
- if (NULL != rc->hr)
- PAIVANA_HTTPD_reverse_cleanup (rc->hr);
- if (NULL != rc->hp)
- PAIVANA_HTTPD_payment_destroy (rc->hp);
- GNUNET_free (rc->url);
- GNUNET_free (rc);
- *con_cls = NULL;
-}
-
-
-/**
- * Function called when MHD first processes an incoming connection.
- * Gives us the respective URI information.
- *
- * We use this to associate the `struct MHD_Connection` with our
- * internal `struct HttpRequest` data structure (by checking
- * for matching sockets).
- *
- * @param cls the HTTP server handle (a `struct MhdHttpList`)
- * @param url the URL that is being requested
- * @param connection MHD connection object for the request
- * @return the `struct RequestContext` that this @a connection is for
- */
-static void *
-mhd_log_callback (void *cls,
- const char *url,
- struct MHD_Connection *connection)
-{
- struct RequestContext *rc;
-
- rc = GNUNET_new (struct RequestContext);
- rc->connection = connection;
- rc->url = GNUNET_strdup (url);
- rc->do_forward = (1 == no_check);
- return rc;
-}
-
-
-/**
- * Callback invoked on every listen socket to start the
- * respective MHD HTTP daemon.
- *
- * @param cls unused
- * @param lsock the listen socket
- */
-static void
-start_daemon (void *cls,
- int lsock)
-{
- struct MHD_Daemon *mhd;
-
- (void) cls;
- GNUNET_assert (-1 != lsock);
- mhd = MHD_start_daemon (
- MHD_USE_DEBUG
- | MHD_ALLOW_SUSPEND_RESUME
- | MHD_USE_DUAL_STACK,
- 0,
- NULL, NULL,
- &create_response, NULL,
- MHD_OPTION_LISTEN_SOCKET,
- lsock,
- MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
- MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
- MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
- MHD_OPTION_END);
-
- if (NULL == mhd)
- {
- GNUNET_break (0);
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- have_daemons = true;
- TALER_MHD_daemon_start (mhd);
-}
-
-
-static void
-serve_requests (void *cls)
-{
- enum GNUNET_GenericReturnValue ret;
-
- ret = TALER_MHD_listen_bind (cfg,
- "paivana",
- &start_daemon,
- NULL);
- switch (ret)
- {
- case GNUNET_SYSERR:
- global_ret = EXIT_NOTCONFIGURED;
- GNUNET_SCHEDULER_shutdown ();
- return;
- case GNUNET_NO:
- if (! have_daemons)
- {
- global_ret = EXIT_NOTCONFIGURED;
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Could not open all configured listen sockets\n");
- break;
- case GNUNET_OK:
- break;
- }
-
-}
-
/* *************** General / main code *************** */
@@ -417,57 +100,6 @@ do_shutdown (void *cls)
/**
- * Try to initialize the paywall response.
- */
-static bool
-load_paywall (void)
-{
- char *tpath;
- char *fn;
- int fd;
- struct stat sb;
-
- tpath = GNUNET_OS_installation_get_path (PAIVANA_project_data (),
- GNUNET_OS_IPK_DATADIR);
- GNUNET_asprintf (&fn,
- "%s/paywall.html",
- tpath);
- GNUNET_free (tpath);
- fd = open (fn,
- O_RDONLY);
- if (-1 == fd)
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "open",
- fn);
- GNUNET_free (fn);
- return false;
- }
- if (0 !=
- fstat (fd,
- &sb))
- {
- GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
- "stat",
- fn);
- GNUNET_free (fn);
- GNUNET_break (0 == close (fd));
- return false;
- }
- GNUNET_free (fn);
- paywall = MHD_create_response_from_fd (sb.st_size,
- fd);
- if (NULL == paywall)
- return false;
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (paywall,
- MHD_HTTP_HEADER_CONTENT_TYPE,
- "text/html"));
- return true;
-}
-
-
-/**
* Main function that will be run. Main tasks are (1) init. the
* curl infrastructure (curl_global_init() / curl_multi_init()),
* then fetch the HTTP port where its Web service should listen at,
@@ -491,11 +123,6 @@ run (void *cls,
(void) cfgfile;
cfg = c;
- if (! load_paywall ())
- {
- GNUNET_SCHEDULER_shutdown ();
- return;
- }
if (! PAIVANA_HTTPD_reverse_init ())
{
GNUNET_SCHEDULER_shutdown ();
@@ -627,8 +254,8 @@ run (void *cls,
GNUNET_free (auth_header);
}
ctx_rc = GNUNET_CURL_gnunet_rc_create (PH_ctx);
- PAIVANA_HTTPD_load_templates (&serve_requests,
- NULL);
+ PAIVANA_HTTPD_load_templates (&PAIVANA_HTTPD_serve_requests,
+ (void *) c);
}
@@ -645,7 +272,7 @@ main (int argc,
"no-payment",
gettext_noop (
"disables payment, useful for testing reverse-proxy only"),
- &no_check),
+ &PH_no_check),
GNUNET_GETOPT_OPTION_END
};
enum GNUNET_GenericReturnValue ret;
@@ -662,7 +289,7 @@ main (int argc,
return EXIT_INVALIDARGUMENT;
if (GNUNET_NO == ret)
return EXIT_SUCCESS;
- return global_ret;
+ return PH_global_ret;
}
diff --git a/src/backend/paivana-httpd.h b/src/backend/paivana-httpd.h
@@ -59,4 +59,14 @@ extern char *PH_base_url;
*/
extern struct GNUNET_CURL_Context *PH_ctx;
+/**
+ * Disable paywall check.
+ */
+extern int PH_no_check;
+
+/**
+ * Value to return from main()
+ */
+extern int PH_global_ret;
+
#endif
diff --git a/src/backend/paivana-httpd_daemon.c b/src/backend/paivana-httpd_daemon.c
@@ -0,0 +1,344 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2026 Taler Systems SA
+
+ Paivana 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.
+
+ Paivana 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 Paivana; see the file COPYING. If not,
+ write to the Free Software Foundation, Inc., 51 Franklin
+ Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @author Christian Grothoff
+ * @file paivana-httpd_daemon.c
+ * @brief daemon functions
+ */
+
+#include "platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
+#include <taler/taler_mhd_lib.h>
+#include "paivana-httpd_cookie.h"
+#include "paivana-httpd_daemon.h"
+#include "paivana-httpd_helper.h"
+#include "paivana-httpd_pay.h"
+#include "paivana-httpd_reverse.h"
+#include "paivana-httpd_templates.h"
+
+
+struct RequestContext
+{
+
+ /**
+ * HTTP connection to the client.
+ */
+ struct MHD_Connection *connection;
+
+ /**
+ * Handle for request forwarding as reverse proxy.
+ */
+ struct HttpRequest *hr;
+
+ /**
+ * Handle for processing actual payment.
+ */
+ struct PayRequest *hp;
+
+ /**
+ * Full request URL.
+ */
+ char *url;
+
+ /**
+ * True if this is a POST to the .well-known/paivana endpoint.
+ */
+ bool is_paivana;
+
+ /**
+ * We are past the paywall, forward to client.
+ */
+ bool do_forward;
+};
+
+
+/**
+ * Set to true if we started a daemon.
+ */
+static bool have_daemons;
+
+
+/**
+ * Main MHD callback for handling requests.
+ *
+ * @param cls unused
+ * @param con MHD connection handle
+ * @param url the url in the request
+ * @param meth the HTTP method used ("GET", "PUT", etc.)
+ * @param ver the HTTP version string (i.e. "HTTP/1.1")
+ * @param upload_data the data being uploaded (excluding HEADERS,
+ * for a POST that fits into memory and that is encoded
+ * with a supported encoding, the POST data will NOT be
+ * given in upload_data and is instead available as
+ * part of MHD_get_connection_values; very large POST
+ * data *will* be made available incrementally in
+ * upload_data)
+ * @param upload_data_size set initially to the size of the
+ * @a upload_data provided; the method must update this
+ * value to the number of bytes NOT processed;
+ * @param con_cls pointer to location where we store the
+ * 'struct Request'
+ * @return #MHD_YES if the connection was handled successfully,
+ * #MHD_NO if the socket must be closed due to a serious
+ * error while handling the request
+ */
+static enum MHD_Result
+create_response (void *cls,
+ struct MHD_Connection *con,
+ const char *url,
+ const char *meth,
+ const char *ver,
+ const char *upload_data,
+ size_t *upload_data_size,
+ void **con_cls)
+{
+ struct RequestContext *rc = *con_cls;
+ const char *cookie;
+
+ (void) cls;
+ if ( (! rc->is_paivana) &&
+ (0 == strcmp (url,
+ ".well-known/paivana")) &&
+ (0 == strcasecmp (meth,
+ "POST")) )
+ {
+ rc->is_paivana = true;
+ }
+ if (rc->is_paivana)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Client POSTed payment, checking validity\n");
+ if (NULL == rc->hp)
+ rc->hp = PAIVANA_HTTPD_payment_create (rc->connection);
+ return PAIVANA_HTTPD_payment_handle (rc->hp,
+ upload_data,
+ upload_data_size);
+ }
+
+ if (rc->do_forward)
+ {
+ if (NULL == rc->hr)
+ rc->hr = PAIVANA_HTTPD_reverse_create (rc->connection,
+ rc->url);
+ return PAIVANA_HTTPD_reverse (rc->hr,
+ con,
+ url,
+ meth,
+ ver,
+ upload_data,
+ upload_data_size);
+ }
+
+ cookie = MHD_lookup_connection_value (con,
+ MHD_COOKIE_KIND,
+ "Paivana-Cookie");
+ if (NULL != cookie)
+ {
+ void *ca;
+ size_t ca_len;
+ struct GNUNET_Buffer buf;
+ char *website;
+ bool ok;
+
+ GNUNET_break (PAIVANA_HTTPD_get_client_address (con,
+ &ca,
+ &ca_len));
+ if (! PAIVANA_HTTPD_get_base_url (con,
+ &buf))
+ {
+ GNUNET_break (0);
+ return TALER_MHD_reply_with_error (
+ con,
+ MHD_HTTP_BAD_REQUEST,
+ TALER_EC_GENERIC_HTTP_HEADERS_MALFORMED,
+ "Host or X-Forwarded-Host required");
+ }
+ GNUNET_buffer_write_str (&buf,
+ url);
+ website = GNUNET_buffer_reap_str (&buf);
+ ok = PAIVANA_HTTPD_check_cookie (cookie,
+ website,
+ ca_len,
+ ca);
+ GNUNET_free (ca);
+ if (! ok)
+ {
+ struct MHD_Response *paywall;
+
+ paywall = PAIVANA_HTTPD_search_templates (website);
+ GNUNET_free (website);
+ if (NULL != paywall)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Request denied\n");
+ return MHD_queue_response (con,
+ MHD_HTTP_PAYMENT_REQUIRED,
+ paywall);
+ }
+ }
+ GNUNET_free (website);
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Request ok!\n");
+ rc->do_forward = true;
+ /* TODO: hacks for 100 continue suppression should go here! */
+ return MHD_YES;
+}
+
+
+/**
+ * Function called when MHD decides that we
+ * are done with a request.
+ *
+ * @param cls NULL
+ * @param connection connection handle
+ * @param con_cls value as set by the last call to
+ * the MHD_AccessHandlerCallback, should be
+ * our `struct RequestContext *` (created in `mhd_log_callback()`)
+ * @param toe reason for request termination (ignored)
+ */
+static void
+mhd_completed_cb (void *cls,
+ struct MHD_Connection *connection,
+ void **con_cls,
+ enum MHD_RequestTerminationCode toe)
+{
+ struct RequestContext *rc = *con_cls;
+
+ (void) cls;
+ (void) connection;
+ if (NULL == rc)
+ return;
+ if (MHD_REQUEST_TERMINATED_COMPLETED_OK != toe)
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "MHD encountered error handling request to %s: %d\n",
+ rc->url,
+ toe);
+ if (NULL != rc->hr)
+ PAIVANA_HTTPD_reverse_cleanup (rc->hr);
+ if (NULL != rc->hp)
+ PAIVANA_HTTPD_payment_destroy (rc->hp);
+ GNUNET_free (rc->url);
+ GNUNET_free (rc);
+ *con_cls = NULL;
+}
+
+
+/**
+ * Function called when MHD first processes an incoming connection.
+ * Gives us the respective URI information.
+ *
+ * We use this to associate the `struct MHD_Connection` with our
+ * internal `struct HttpRequest` data structure (by checking
+ * for matching sockets).
+ *
+ * @param cls the HTTP server handle (a `struct MhdHttpList`)
+ * @param url the URL that is being requested
+ * @param connection MHD connection object for the request
+ * @return the `struct RequestContext` that this @a connection is for
+ */
+static void *
+mhd_log_callback (void *cls,
+ const char *url,
+ struct MHD_Connection *connection)
+{
+ struct RequestContext *rc;
+
+ rc = GNUNET_new (struct RequestContext);
+ rc->connection = connection;
+ rc->url = GNUNET_strdup (url);
+ rc->do_forward = (1 == PH_no_check);
+ return rc;
+}
+
+
+/**
+ * Callback invoked on every listen socket to start the
+ * respective MHD HTTP daemon.
+ *
+ * @param cls unused
+ * @param lsock the listen socket
+ */
+static void
+start_daemon (void *cls,
+ int lsock)
+{
+ struct MHD_Daemon *mhd;
+
+ (void) cls;
+ GNUNET_assert (-1 != lsock);
+ mhd = MHD_start_daemon (
+ MHD_USE_DEBUG
+ | MHD_ALLOW_SUSPEND_RESUME
+ | MHD_USE_DUAL_STACK,
+ 0,
+ NULL, NULL,
+ &create_response, NULL,
+ MHD_OPTION_LISTEN_SOCKET,
+ lsock,
+ MHD_OPTION_CONNECTION_TIMEOUT, (unsigned int) 16,
+ MHD_OPTION_NOTIFY_COMPLETED, &mhd_completed_cb, NULL,
+ MHD_OPTION_URI_LOG_CALLBACK, &mhd_log_callback, NULL,
+ MHD_OPTION_END);
+
+ if (NULL == mhd)
+ {
+ GNUNET_break (0);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ have_daemons = true;
+ TALER_MHD_daemon_start (mhd);
+}
+
+
+void
+PAIVANA_HTTPD_serve_requests (void *cls)
+{
+ const struct GNUNET_CONFIGURATION_Handle *cfg = cls;
+ enum GNUNET_GenericReturnValue ret;
+
+ ret = TALER_MHD_listen_bind (cfg,
+ "paivana",
+ &start_daemon,
+ NULL);
+ switch (ret)
+ {
+ case GNUNET_SYSERR:
+ PH_global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ case GNUNET_NO:
+ if (! have_daemons)
+ {
+ PH_global_ret = EXIT_NOTCONFIGURED;
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
+ "Could not open all configured listen sockets\n");
+ break;
+ case GNUNET_OK:
+ break;
+ }
+}
diff --git a/src/backend/paivana-httpd_daemon.h b/src/backend/paivana-httpd_daemon.h
@@ -0,0 +1,43 @@
+/*
+ This file is part of GNUnet.
+ Copyright (C) 2026 Taler Systems SA
+
+ Paivana 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.
+
+ Paivana 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 Paivana; see the file COPYING. If not,
+ write to the Free Software Foundation, Inc., 51 Franklin
+ Street, Fifth Floor, Boston, MA 02110-1301, USA.
+*/
+
+/**
+ * @author Christian Grothoff
+ * @file paivana-httpd_daemon.h
+ * @brief daemon functions
+ */
+#ifndef PAIVANA_HTTPD_DAEMON_H
+#define PAIVANA_HTTPD_DAEMON_H
+
+#include <stdbool.h>
+#include <microhttpd.h>
+#include <gnunet/gnunet_util_lib.h>
+
+
+/**
+ * Start the HTTP server and begin serving requests.
+ *
+ * @param cls pass the `const struct GNUNET_CONFIGURATION_Handle`
+ */
+void
+PAIVANA_HTTPD_serve_requests (void *cls);
+
+
+#endif
diff --git a/src/backend/paivana-httpd_templates.c b/src/backend/paivana-httpd_templates.c
@@ -23,9 +23,72 @@
* @file paivana-httpd_templates.c
* @brief template functions
*/
+#include "platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_util_lib.h>
+#include <gnunet/gnunet_curl_lib.h>
#include "paivana-httpd.h"
#include "paivana-httpd_templates.h"
#include <taler/taler_mhd_lib.h>
+#include "paivana_pd.h"
+
+
+// FIXME: make part of our template structure...
+/**
+ * Static paywall response.
+ */
+static struct MHD_Response *paywall;
+
+
+/**
+ * Try to initialize the paywall response.
+ */
+static bool
+load_paywall (void)
+{
+ char *tpath;
+ char *fn;
+ int fd;
+ struct stat sb;
+
+ tpath = GNUNET_OS_installation_get_path (PAIVANA_project_data (),
+ GNUNET_OS_IPK_DATADIR);
+ GNUNET_asprintf (&fn,
+ "%s/paywall.html",
+ tpath);
+ GNUNET_free (tpath);
+ fd = open (fn,
+ O_RDONLY);
+ if (-1 == fd)
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "open",
+ fn);
+ GNUNET_free (fn);
+ return false;
+ }
+ if (0 !=
+ fstat (fd,
+ &sb))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "stat",
+ fn);
+ GNUNET_free (fn);
+ GNUNET_break (0 == close (fd));
+ return false;
+ }
+ GNUNET_free (fn);
+ paywall = MHD_create_response_from_fd (sb.st_size,
+ fd);
+ if (NULL == paywall)
+ return false;
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (paywall,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ "text/html"));
+ return true;
+}
void
@@ -35,12 +98,20 @@ PAIVANA_HTTPD_load_templates (GNUNET_SCHEDULER_TaskCallback cb,
// FIXME: not implemented!
GNUNET_SCHEDULER_add_now (cb,
cb_cls);
+ // -> Note: needs *restart* on template changes!
+ // -> in future: long-poll on GET /private/templates?
}
struct MHD_Response *
PAIVANA_HTTPD_search_templates (const char *website)
{
+ // FIXME: check if url is one that we require payment for,
+ // if not return NULL
+ // (also should determine WHICH payment template
+ // we use => load *all* templates of the instance,
+ // pre-compile *all* regexes and then match!)
+
// FIXME: not implemented
return NULL;
}