paivana

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

commit 40ed79e41cc691e257ebc8ee5b974003f7bed1b0
parent ecfb6f19b1f9e113bc0ead00db9fde8188581084
Author: Christian Grothoff <christian@grothoff.org>
Date:   Mon, 20 Apr 2026 00:34:03 +0200

expand consistency check logic

Diffstat:
Msrc/backend/paivana-httpd.c | 7+++++--
Msrc/backend/paivana-httpd_pay.c | 88++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 86 insertions(+), 9 deletions(-)

diff --git a/src/backend/paivana-httpd.c b/src/backend/paivana-httpd.c @@ -169,8 +169,11 @@ create_response (void *cls, } // FIXME: check if url is one that we require payment for, // if not set 'do_forward = true'. - // (also should eventually determine WHICH payment template - // we use...) + // (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) { diff --git a/src/backend/paivana-httpd_pay.c b/src/backend/paivana-httpd_pay.c @@ -21,7 +21,6 @@ /** * @author Christian Grothoff * @file paivana-httpd_pay.c - * * @brief payment processing logic */ #include <gnunet/gnunet_util_lib.h> @@ -143,6 +142,84 @@ PAIVANA_HTTPD_payment_create (struct MHD_Connection *connection) /** + * Check that the @a contract that was paid is reasonable for the + * request in @a ph, that is that we would indeed consider this + * contract to apply for the website and duration indicated + * in @a ph. If it does not apply, a response must be set in + * @a ph. + * + * @param[in,out] ph request to check + * @param contract contract to check + * @return true if the contract is good for the request, + * false if not and thus a response object was created in @a ph + */ +static bool +check_contract (struct PayRequest *ph, + const json_t *contract) +{ + struct GNUNET_TIME_Timestamp max_time + = GNUNET_TIME_UNIT_FOREVER_TS; + const char *regex_s = NULL; + // FIXME: expand Paivana contract template specifications with + // these fields! + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("website_regex", + &regex_s), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_timestamp ("max_time", + &max_time), + NULL), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue ret; + const char *ename; + unsigned int eline; + + ret = GNUNET_JSON_parse (contract, + spec, + &ename, + &eline); + if (GNUNET_OK != ret) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Encountered contract with unexpected fields: %s@%u\n", + ename, + eline); + return true; + } + if (NULL != regex_s) + { + bool match = true; + + // FIXME: check ph->website matches regex. + // If not: => TALER_EC_PAIVANA_WRONG_ORDER with 409! + if (! match) + { + GNUNET_break_op (0); + ph->response = TALER_MHD_make_error (TALER_EC_PAIVANA_WRONG_ORDER, + ph->order_id); + ph->response_status = MHD_HTTP_CONFLICT; + return false; + } + } + if (GNUNET_TIME_timestamp_cmp (ph->cur_time, + >, + max_time)) + { + GNUNET_break_op (0); + // FIXME: bad EC! Introduce new one! + ph->response = TALER_MHD_make_error (TALER_EC_PAIVANA_WRONG_ORDER, + ph->order_id); + ph->response_status = MHD_HTTP_GONE; + return false; + } + return true; +} + + +/** * Handle response from the GET /private/orders/$ORDER_ID request. * * @param ph the payment request we are processing @@ -191,12 +268,9 @@ order_status_cb (struct PayRequest *ph, ca_len = 0; break; } - // FIXME: check ph->website matches template that - // was paid here. If not: - // => TALER_EC_PAIVANA_WRONG_ORDER with 409! - // FIXME: check ph->cur_time is not too far into the - // future (if purchase is for limited time!). If so: - // => response with 410! + if (! check_contract (ph, + osr->details.ok.details.paid.contract_terms)) + return; cookie = PAIVANA_HTTPD_compute_cookie (ph->cur_time, ph->website, ca_len,