paivana

HTTP paywall reverse proxy
Log | Files | Refs | README | LICENSE

commit 6c993afa87f882acf31de13af4f551b5f8171f0e
parent 54306aeb704d7078f93f4987511dc6f5f64399ed
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 20 Apr 2026 15:34:43 +0200

more basics on Paivana

Diffstat:
Msrc/backend/Makefile.am | 1+
Msrc/backend/paivana-httpd.c | 391++-----------------------------------------------------------------------------
Msrc/backend/paivana-httpd.h | 10++++++++++
Asrc/backend/paivana-httpd_daemon.c | 344+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/backend/paivana-httpd_daemon.h | 43+++++++++++++++++++++++++++++++++++++++++++
Msrc/backend/paivana-httpd_templates.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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; }