commit 10d0e97f191a8bd7de07049559a2eccb29a78507
parent f0752580e0a1fb8ae2afa25ee12fad2bf594294b
Author: Christian Grothoff <christian@grothoff.org>
Date: Sun, 2 Mar 2025 12:13:52 +0100
more porting to MHD2
Diffstat:
4 files changed, 417 insertions(+), 480 deletions(-)
diff --git a/src/sync/sync-httpd2.c b/src/sync/sync-httpd2.c
@@ -500,9 +500,9 @@ run (void *cls,
GNUNET_SCHEDULER_shutdown ();
return;
}
- fh = TALER_MHD2_bind (config,
- "sync",
- &port);
+ fh = TALER_MHD_bind (config,
+ "sync",
+ &port);
if ( (0 == port) &&
(-1 == fh) )
{
diff --git a/src/sync/sync-httpd2_backup-post.c b/src/sync/sync-httpd2_backup-post.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2019 Taler Systems SA
+ Copyright (C) 2019--2025 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
@@ -19,10 +19,12 @@
* @author Christian Grothoff
*/
#include "platform.h"
-#include "sync-httpd.h"
+#include "sync-httpd2.h"
#include <gnunet/gnunet_util_lib.h>
-#include "sync-httpd_backup.h"
+#include "sync-httpd2_backup.h"
+#include <microhttpd2.h>
#include <taler/taler_json_lib.h>
+#include <taler/taler_mhd2_lib.h>
#include <taler/taler_merchant_service.h>
#include <taler/taler_signatures.h>
@@ -73,11 +75,6 @@ struct BackupContext
struct TALER_ClaimTokenP token;
/**
- * Hash context for the upload.
- */
- struct GNUNET_HashContext *hash_ctx;
-
- /**
* Kept in DLL for shutdown handling while suspended.
*/
struct BackupContext *next;
@@ -90,12 +87,12 @@ struct BackupContext
/**
* Used while suspended for resumption.
*/
- struct MHD_Connection *con;
+ struct MHD_Request *request;
/**
- * Upload, with as many bytes as we have received so far.
+ * To be returned upon resuming.
*/
- char *upload;
+ struct MHD_Response *resp;
/**
* Used while we are awaiting proposal creation.
@@ -108,12 +105,6 @@ struct BackupContext
struct TALER_MERCHANT_OrderMerchantGetHandle *omgh;
/**
- * HTTP response code to use on resume, if non-NULL.
-
- */
- struct MHD_Response *resp;
-
- /**
* Order under which the client promised payment, or NULL.
*/
const char *order_id;
@@ -130,21 +121,6 @@ struct BackupContext
struct GNUNET_TIME_Timestamp existing_order_timestamp;
/**
- * Expected total upload size.
- */
- size_t upload_size;
-
- /**
- * Current offset for the upload.
- */
- size_t upload_off;
-
- /**
- * HTTP response code to use on resume, if resp is set.
- */
- unsigned int response_code;
-
- /**
* Do not look for an existing order, force a fresh order to be created.
*/
bool force_fresh_order;
@@ -163,7 +139,7 @@ static struct BackupContext *bc_tail;
/**
- * Service is shutting down, resume all MHD connections NOW.
+ * Service is shutting down, resume all MHD requests NOW.
*/
void
SH_resume_all_bc ()
@@ -175,7 +151,7 @@ SH_resume_all_bc ()
GNUNET_CONTAINER_DLL_remove (bc_head,
bc_tail,
bc);
- MHD_resume_connection (bc->con);
+ MHD_request_resume (bc->request);
if (NULL != bc->po)
{
TALER_MERCHANT_orders_post_cancel (bc->po);
@@ -193,27 +169,27 @@ SH_resume_all_bc ()
/**
* Function called to clean up a backup context.
*
- * @param hc a `struct BackupContext`
+ * @param cls the `struct BackupContext`
+ * @param data the details about the event
+ * @param request_app_context the application request context
*/
static void
-cleanup_ctx (struct TM_HandlerContext *hc)
+cleanup_ctx (void *cls,
+ const struct MHD_RequestEndedData *data,
+ void *request_app_context)
{
- struct BackupContext *bc = (struct BackupContext *) hc;
+ struct BackupContext *bc = cls;
+ GNUNET_assert (cls == request_app_context);
if (NULL != bc->po)
TALER_MERCHANT_orders_post_cancel (bc->po);
- if (NULL != bc->hash_ctx)
- GNUNET_CRYPTO_hash_context_abort (bc->hash_ctx);
- if (NULL != bc->resp)
- MHD_destroy_response (bc->resp);
GNUNET_free (bc->existing_order_id);
- GNUNET_free (bc->upload);
GNUNET_free (bc);
}
/**
- * Transmit a payment request for @a order_id on @a connection
+ * Transmit a payment request for @a order_id on @a request
*
* @param order_id our backend's order ID
* @param token the claim token generated by the merchant (NULL if
@@ -230,10 +206,8 @@ make_payment_request (const char *order_id,
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Creating payment request for order `%s'\n",
order_id);
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- TALER_MHD_add_global_headers (resp);
+ resp = MHD_response_from_empty (MHD_HTTP_STATUS_PAYMENT_REQUIRED);
+ TALER_MHD2_add_global_headers (resp);
{
char *hdr;
const char *pfx;
@@ -257,13 +231,13 @@ make_payment_request (const char *order_id,
else
{
GNUNET_break (0);
- MHD_destroy_response (resp);
+ MHD_response_destroy (resp);
return NULL;
}
if (0 == strlen (hn))
{
GNUNET_break (0);
- MHD_destroy_response (resp);
+ MHD_response_destroy (resp);
return NULL;
}
@@ -276,11 +250,13 @@ make_payment_request (const char *order_id,
if (NULL != token)
{
GNUNET_buffer_write_str (&hdr_buf, "?c=");
- GNUNET_buffer_write_data_encoded (&hdr_buf, token, sizeof (*token));
+ GNUNET_buffer_write_data_encoded (&hdr_buf,
+ token,
+ sizeof (*token));
}
hdr = GNUNET_buffer_reap_str (&hdr_buf);
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (resp,
+ GNUNET_break (MHD_SC_OK ==
+ MHD_response_add_header (resp,
"Taler",
hdr));
GNUNET_free (hdr);
@@ -308,18 +284,19 @@ proposal_cb (void *cls,
bc_tail,
bc);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Resuming connection with order `%s'\n",
+ "Resuming request with order `%s'\n",
bc->order_id);
- MHD_resume_connection (bc->con);
+ MHD_request_resume (bc->request);
SH_trigger_daemon ();
- if (MHD_HTTP_OK != por->hr.http_status)
+ if (MHD_HTTP_STATUS_OK != por->hr.http_status)
{
GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
"Backend returned status %u/%u\n",
por->hr.http_status,
(unsigned int) por->hr.ec);
GNUNET_break_op (0);
- bc->resp = TALER_MHD_MAKE_JSON_PACK (
+ bc->resp = TALER_MHD2_MAKE_JSON_PACK (
+ MHD_HTTP_STATUS_BAD_GATEWAY,
TALER_JSON_pack_ec (TALER_EC_SYNC_PAYMENT_CREATE_BACKEND_ERROR),
GNUNET_JSON_pack_uint64 ("backend-ec",
por->hr.ec),
@@ -328,7 +305,6 @@ proposal_cb (void *cls,
GNUNET_JSON_pack_allow_null (
GNUNET_JSON_pack_object_incref ("backend-reply",
(json_t *) por->hr.reply)));
- bc->response_code = MHD_HTTP_BAD_GATEWAY;
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -342,10 +318,9 @@ proposal_cb (void *cls,
if (0 >= qs)
{
GNUNET_break (0);
- bc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
- "Failed to persist payment request in sync database");
+ bc->resp = TALER_MHD2_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
+ "Failed to persist payment request in sync database");
GNUNET_assert (NULL != bc->resp);
- bc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -354,7 +329,6 @@ proposal_cb (void *cls,
bc->resp = make_payment_request (por->details.ok.order_id,
por->details.ok.token);
GNUNET_assert (NULL != bc->resp);
- bc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
}
@@ -413,23 +387,22 @@ check_payment_cb (void *cls,
GNUNET_CONTAINER_DLL_remove (bc_head,
bc_tail,
bc);
- MHD_resume_connection (bc->con);
+ MHD_request_resume (bc->request);
SH_trigger_daemon ();
switch (hr->http_status)
{
case 0:
/* Likely timeout, complain! */
- bc->response_code = MHD_HTTP_GATEWAY_TIMEOUT;
- bc->resp = TALER_MHD_make_error (
+ bc->resp = TALER_MHD2_make_error (
TALER_EC_SYNC_GENERIC_BACKEND_TIMEOUT,
NULL);
return;
- case MHD_HTTP_OK:
+ case MHD_HTTP_STATUS_OK:
break; /* handled below */
default:
/* Unexpected backend response */
- bc->response_code = MHD_HTTP_BAD_GATEWAY;
- bc->resp = TALER_MHD_MAKE_JSON_PACK (
+ bc->resp = TALER_MHD2_MAKE_JSON_PACK (
+ MHD_HTTP_STATUS_BAD_GATEWAY,
GNUNET_JSON_pack_uint64 ("code",
TALER_EC_SYNC_GENERIC_BACKEND_ERROR),
GNUNET_JSON_pack_string ("hint",
@@ -445,7 +418,7 @@ check_payment_cb (void *cls,
return;
}
- GNUNET_assert (MHD_HTTP_OK == hr->http_status);
+ GNUNET_assert (MHD_HTTP_STATUS_OK == hr->http_status);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Payment status checked: %d\n",
osr->details.ok.status);
@@ -462,10 +435,9 @@ check_payment_cb (void *cls,
if (0 <= qs)
return; /* continue as planned */
GNUNET_break (0);
- bc->resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
- "increment lifetime");
+ bc->resp = TALER_MHD2_make_error (TALER_EC_GENERIC_DB_STORE_FAILED,
+ "increment lifetime");
GNUNET_assert (NULL != bc->resp);
- bc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
return; /* continue as planned */
}
case TALER_MERCHANT_OSC_UNPAID:
@@ -482,15 +454,13 @@ check_payment_cb (void *cls,
? NULL
: &bc->token);
GNUNET_assert (NULL != bc->resp);
- bc->response_code = MHD_HTTP_PAYMENT_REQUIRED;
return;
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Timeout waiting for payment\n");
- bc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT,
- "Timeout awaiting promised payment");
+ bc->resp = TALER_MHD2_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT,
+ "Timeout awaiting promised payment");
GNUNET_assert (NULL != bc->resp);
- bc->response_code = MHD_HTTP_REQUEST_TIMEOUT;
}
@@ -510,7 +480,6 @@ await_payment (struct BackupContext *bc,
GNUNET_CONTAINER_DLL_insert (bc_head,
bc_tail,
bc);
- MHD_suspend_connection (bc->con);
bc->order_id = order_id;
bc->omgh = TALER_MERCHANT_merchant_order_get (SH_ctx,
SH_backend_url,
@@ -527,21 +496,24 @@ await_payment (struct BackupContext *bc,
/**
* Helper function used to ask our backend to begin
* processing a payment for the user's account.
- * May perform asynchronous operations by suspending the connection
+ * May perform asynchronous operations by suspending the request
* if required.
*
* @param bc context to begin payment for.
* @param pay_req #GNUNET_YES if payment was explicitly requested,
* #GNUNET_NO if payment is needed
- * @return MHD status code
+ * @param[out] suspend set to true if the request should be suspended
+ * @return MHD response
*/
-static MHD_RESULT
+static struct MHD_Response *
begin_payment (struct BackupContext *bc,
- int pay_req)
+ int pay_req,
+ bool *suspend)
{
static const char *no_uuids[1] = { NULL };
json_t *order;
+ *suspend = false;
if (! bc->force_fresh_order)
{
enum GNUNET_DB_QueryStatus qs;
@@ -552,17 +524,9 @@ begin_payment (struct BackupContext *bc,
bc);
if (qs < 0)
{
- struct MHD_Response *resp;
- MHD_RESULT ret;
-
- resp = TALER_MHD_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
- "pending payments");
- ret = MHD_queue_response (bc->con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- resp);
- GNUNET_break (MHD_YES == ret);
- MHD_destroy_response (resp);
- return ret;
+ GNUNET_break (0);
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
+ "pending payments");
}
if (NULL != bc->existing_order_id)
{
@@ -572,16 +536,17 @@ begin_payment (struct BackupContext *bc,
await_payment (bc,
GNUNET_TIME_UNIT_ZERO /* no long polling */,
bc->existing_order_id);
- return MHD_YES;
+ *suspend = true;
+ return NULL;
}
}
GNUNET_CONTAINER_DLL_insert (bc_head,
bc_tail,
bc);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Suspending connection while creating order at `%s'\n",
+ "Suspending request while creating order at `%s'\n",
SH_backend_url);
- MHD_suspend_connection (bc->con);
+ *suspend = true;
order = GNUNET_JSON_PACK (
TALER_JSON_pack_amount ("amount",
&SH_annual_fee),
@@ -603,366 +568,136 @@ begin_payment (struct BackupContext *bc,
bc);
SH_trigger_curl ();
json_decref (order);
- return MHD_YES;
+ return NULL;
}
/**
* We got some query status from the DB. Handle the error cases.
- * May perform asynchronous operations by suspending the connection
+ * May perform asynchronous operations by suspending the request
* if required.
*
- * @param bc connection to handle status for
+ * @param bc request to handle status for
* @param qs query status to handle
- * @return #MHD_YES or #MHD_NO
+ * @param[out] suspend set to true if the request should be suspended
+ * @return MHD response to return
*/
-static MHD_RESULT
+static struct MHD_Response *
handle_database_error (struct BackupContext *bc,
- enum SYNC_DB_QueryStatus qs)
+ enum SYNC_DB_QueryStatus qs,
+ bool *suspend)
{
+ *suspend = false;
switch (qs)
{
case SYNC_DB_OLD_BACKUP_MISSING:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Update failed: no existing backup\n");
- return TALER_MHD_reply_with_error (bc->con,
- MHD_HTTP_NOT_FOUND,
- TALER_EC_SYNC_PREVIOUS_BACKUP_UNKNOWN,
- NULL);
+ return TALER_MHD2_make_error (TALER_EC_SYNC_PREVIOUS_BACKUP_UNKNOWN,
+ NULL);
case SYNC_DB_OLD_BACKUP_MISMATCH:
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Conflict detected, returning existing backup\n");
- return SH_return_backup (bc->con,
- &bc->account,
- MHD_HTTP_CONFLICT);
+ return SH_make_backup (&bc->account,
+ MHD_HTTP_STATUS_CONFLICT);
case SYNC_DB_PAYMENT_REQUIRED:
{
- const char *order_id;
+ const struct MHD_StringNullable *order_id;
- order_id = MHD_lookup_connection_value (bc->con,
- MHD_GET_ARGUMENT_KIND,
- "paying");
+ order_id = MHD_request_get_value (bc->request,
+ MHD_VK_GET_ARGUMENT,
+ "paying");
if (NULL == order_id)
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Payment required, starting payment process\n");
return begin_payment (bc,
- GNUNET_NO);
+ GNUNET_NO,
+ suspend);
}
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Payment required, awaiting completion of `%s'\n",
- order_id);
+ order_id->cstr);
await_payment (bc,
CHECK_PAYMENT_GENERIC_TIMEOUT,
- order_id);
+ order_id->cstr);
+ *suspend = true;
+ return NULL;
}
- return MHD_YES;
case SYNC_DB_HARD_ERROR:
GNUNET_break (0);
- return TALER_MHD_reply_with_error (bc->con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_COMMIT_FAILED,
- NULL);
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_COMMIT_FAILED,
+ NULL);
case SYNC_DB_SOFT_ERROR:
GNUNET_break (0);
- return TALER_MHD_reply_with_error (bc->con,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_SOFT_FAILURE,
- NULL);
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_SOFT_FAILURE,
+ NULL);
case SYNC_DB_NO_RESULTS:
GNUNET_assert (0);
- return MHD_NO;
+ return NULL;
/* intentional fall-through! */
case SYNC_DB_ONE_RESULT:
GNUNET_assert (0);
- return MHD_NO;
+ return NULL;
}
GNUNET_break (0);
- return MHD_NO;
+ return NULL;
}
-MHD_RESULT
-SH_backup_post (struct MHD_Connection *connection,
- void **con_cls,
- const struct SYNC_AccountPublicKeyP *account,
- const char *upload_data,
- size_t *upload_data_size)
+/**
+ * Function to process data uploaded by a client.
+ *
+ * @param upload_cls our `struct BackupContext *`
+ * @param request the request is being processed
+ * @param content_data_size the size of the @a content_data,
+ * zero when all data have been processed
+ * @param[in] content_data the uploaded content data,
+ * may be modified in the callback,
+ * valid only until return from the callback,
+ * NULL when all data have been processed
+ * @return action specifying how to proceed:
+ * #MHD_upload_action_continue() to continue upload (for incremental
+ * upload processing only),
+ * #MHD_upload_action_suspend() to stop reading the upload until
+ * the request is resumed,
+ * #MHD_upload_action_abort_request() to close the socket,
+ * or a response to discard the rest of the upload and transmit
+ * the response
+ */
+static const struct MHD_UploadAction *
+handle_upload (void *upload_cls,
+ struct MHD_Request *request,
+ size_t content_data_size,
+ void *content_data)
{
- struct BackupContext *bc;
-
- bc = *con_cls;
- if (NULL == bc)
- {
- /* first call, setup internals */
- bc = GNUNET_new (struct BackupContext);
- bc->hc.cc = &cleanup_ctx;
- bc->con = connection;
- bc->account = *account;
- {
- const char *fresh;
-
- fresh = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "fresh");
- if (NULL != fresh)
- bc->force_fresh_order = true;
- }
- *con_cls = bc;
-
- /* now setup 'bc' */
- {
- const char *lens;
- unsigned long len;
-
- lens = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_CONTENT_LENGTH);
- if ( (NULL == lens) ||
- (1 !=
- sscanf (lens,
- "%lu",
- &len)) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (
- connection,
- MHD_HTTP_BAD_REQUEST,
- (NULL == lens)
- ? TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH
- : TALER_EC_SYNC_MISSING_CONTENT_LENGTH,
- lens);
- }
- if (len / 1024 / 1024 >= SH_upload_limit_mb)
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_PAYLOAD_TOO_LARGE,
- TALER_EC_SYNC_EXCESSIVE_CONTENT_LENGTH,
- NULL);
- }
- bc->upload = GNUNET_malloc_large (len);
- if (NULL == bc->upload)
- {
- GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
- "malloc");
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_PAYLOAD_TOO_LARGE,
- TALER_EC_SYNC_OUT_OF_MEMORY_ON_CONTENT_LENGTH,
- NULL);
- }
- bc->upload_size = (size_t) len;
- }
- {
- const char *im;
+ struct BackupContext *bc = upload_cls;
- im = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_IF_MATCH);
- if (NULL != im)
- {
- if ( (2 >= strlen (im)) ||
- ('"' != im[0]) ||
- ('"' != im[strlen (im) - 1]) ||
- (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (im + 1,
- strlen (im) - 2,
- &bc->old_backup_hash,
- sizeof (bc->old_backup_hash))) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_SYNC_BAD_IF_MATCH,
- NULL);
- }
- }
- }
- {
- const char *sig_s;
-
- sig_s = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- "Sync-Signature");
- if ( (NULL == sig_s) ||
- (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (sig_s,
- strlen (sig_s),
- &bc->account_sig,
- sizeof (bc->account_sig))) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_SYNC_BAD_SYNC_SIGNATURE,
- NULL);
- }
- }
- {
- const char *etag;
-
- etag = MHD_lookup_connection_value (connection,
- MHD_HEADER_KIND,
- MHD_HTTP_HEADER_IF_NONE_MATCH);
- if ( (NULL == etag) ||
- (2 >= strlen (etag)) ||
- ('"' != etag[0]) ||
- ('"' != etag[strlen (etag) - 1]) ||
- (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (etag + 1,
- strlen (etag) - 2,
- &bc->new_backup_hash,
- sizeof (bc->new_backup_hash))) )
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_SYNC_BAD_IF_NONE_MATCH,
- NULL);
- }
- }
- /* validate signature */
- {
- struct SYNC_UploadSignaturePS usp = {
- .purpose.size = htonl (sizeof (usp)),
- .purpose.purpose = htonl (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD),
- .old_backup_hash = bc->old_backup_hash,
- .new_backup_hash = bc->new_backup_hash
- };
-
- if (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD,
- &usp,
- &bc->account_sig.eddsa_sig,
- &account->eddsa_pub))
- {
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_FORBIDDEN,
- TALER_EC_SYNC_INVALID_SIGNATURE,
- NULL);
- }
- }
- /* get ready to hash (done here as we may go async for payments next) */
- bc->hash_ctx = GNUNET_CRYPTO_hash_context_start ();
-
- /* Check database to see if the transaction is permissible */
- {
- struct GNUNET_HashCode hc;
- enum SYNC_DB_QueryStatus qs;
-
- qs = db->lookup_account_TR (db->cls,
- account,
- &hc);
- if (qs < 0)
- return handle_database_error (bc,
- qs);
- if (SYNC_DB_NO_RESULTS == qs)
- memset (&hc, 0, sizeof (hc));
- if (0 == GNUNET_memcmp (&hc,
- &bc->new_backup_hash))
- {
- /* Refuse upload: we already have that backup! */
- struct MHD_Response *resp;
- MHD_RESULT ret;
-
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- TALER_MHD_add_global_headers (resp);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NOT_MODIFIED,
- resp);
- GNUNET_break (MHD_YES == ret);
- MHD_destroy_response (resp);
- return ret;
- }
- if (0 != GNUNET_memcmp (&hc,
- &bc->old_backup_hash))
- {
- /* Refuse upload: if-none-match failed! */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Conflict detected, returning existing backup\n");
- return SH_return_backup (connection,
- account,
- MHD_HTTP_CONFLICT);
- }
- }
- /* check if the client insists on paying */
- {
- const char *order_req;
-
- order_req = MHD_lookup_connection_value (connection,
- MHD_GET_ARGUMENT_KIND,
- "pay");
- if (NULL != order_req)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Payment requested, starting payment process\n");
- return begin_payment (bc,
- GNUNET_YES);
- }
- }
- /* ready to begin! */
- return MHD_YES;
- }
- /* handle upload */
- if (0 != *upload_data_size)
- {
- /* check MHD invariant */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Processing %u bytes of upload data\n",
- (unsigned int) *upload_data_size);
- GNUNET_assert (bc->upload_off + *upload_data_size <= bc->upload_size);
- memcpy (&bc->upload[bc->upload_off],
- upload_data,
- *upload_data_size);
- bc->upload_off += *upload_data_size;
- GNUNET_CRYPTO_hash_context_read (bc->hash_ctx,
- upload_data,
- *upload_data_size);
- *upload_data_size = 0;
- return MHD_YES;
- }
- else if ( (0 == bc->upload_off) &&
- (0 != bc->upload_size) &&
- (NULL == bc->resp) )
- {
- /* wait for upload */
- return MHD_YES;
- }
if (NULL != bc->resp)
{
- MHD_RESULT ret;
-
- /* We generated a response asynchronously, queue that */
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Returning asynchronously generated response with HTTP status %u\n",
- bc->response_code);
- ret = MHD_queue_response (connection,
- bc->response_code,
- bc->resp);
- GNUNET_break (MHD_YES == ret);
- MHD_destroy_response (bc->resp);
- bc->resp = NULL;
- return ret;
+ /* This can happen on resume */
+ return MHD_upload_action_from_response (request,
+ bc->resp);
}
-
- /* finished with upload, check hash */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Processing %llu bytes of upload data\n",
+ (unsigned long long) content_data_size);
+ /* Check hash matches */
{
struct GNUNET_HashCode our_hash;
- GNUNET_CRYPTO_hash_context_finish (bc->hash_ctx,
- &our_hash);
- bc->hash_ctx = NULL;
+ GNUNET_CRYPTO_hash (content_data,
+ content_data_size,
+ &our_hash);
if (0 != GNUNET_memcmp (&our_hash,
&bc->new_backup_hash))
{
GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_SYNC_INVALID_UPLOAD,
- NULL);
+ return MHD_upload_action_from_response (
+ request,
+ TALER_MHD2_make_error (
+ TALER_EC_SYNC_INVALID_UPLOAD,
+ NULL));
}
}
@@ -970,67 +705,285 @@ SH_backup_post (struct MHD_Connection *connection,
{
enum SYNC_DB_QueryStatus qs;
- if (GNUNET_YES == GNUNET_is_zero (&bc->old_backup_hash))
+ if (GNUNET_is_zero (&bc->old_backup_hash))
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Uploading first backup to account\n");
qs = db->store_backup_TR (db->cls,
- account,
+ &bc->account,
&bc->account_sig,
&bc->new_backup_hash,
- bc->upload_size,
- bc->upload);
+ content_data_size,
+ content_data);
}
else
{
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Uploading existing backup of account\n");
qs = db->update_backup_TR (db->cls,
- account,
+ &bc->account,
&bc->old_backup_hash,
&bc->account_sig,
&bc->new_backup_hash,
- bc->upload_size,
- bc->upload);
+ content_data_size,
+ content_data);
}
if (qs < 0)
- return handle_database_error (bc,
- qs);
+ {
+ bool suspend;
+ struct MHD_Response *resp;
+
+ resp = handle_database_error (bc,
+ qs,
+ &suspend);
+ if (suspend)
+ return MHD_upload_action_suspend (request);
+ return MHD_upload_action_from_response (request,
+ resp);
+ }
if (0 == qs)
{
/* database says nothing actually changed, 304 (could
theoretically happen if another equivalent upload succeeded
since we last checked!) */
struct MHD_Response *resp;
- MHD_RESULT ret;
-
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- TALER_MHD_add_global_headers (resp);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NOT_MODIFIED,
- resp);
- GNUNET_break (MHD_YES == ret);
- MHD_destroy_response (resp);
- return ret;
+
+ resp = MHD_response_from_empty (MHD_HTTP_STATUS_NOT_MODIFIED);
+ TALER_MHD2_add_global_headers (resp);
+ return MHD_upload_action_from_response (request,
+ resp);
}
}
/* generate main (204) standard success reply */
{
struct MHD_Response *resp;
- MHD_RESULT ret;
-
- resp = MHD_create_response_from_buffer (0,
- NULL,
- MHD_RESPMEM_PERSISTENT);
- TALER_MHD_add_global_headers (resp);
- ret = MHD_queue_response (connection,
- MHD_HTTP_NO_CONTENT,
- resp);
- GNUNET_break (MHD_YES == ret);
- MHD_destroy_response (resp);
- return ret;
+
+ resp = MHD_response_from_empty (MHD_HTTP_STATUS_NO_CONTENT);
+ TALER_MHD2_add_global_headers (resp);
+ return MHD_upload_action_from_response (request,
+ resp);
+ }
+}
+
+
+const struct MHD_Action *
+SH_backup_post (struct MHD_Request *request,
+ const struct SYNC_AccountPublicKeyP *account,
+ uint_fast64_t upload_size)
+{
+ struct BackupContext *bc;
+ union MHD_RequestInfoFixedData fi;
+
+ GNUNET_assert (MHD_SC_OK ==
+ MHD_request_get_info_fixed (
+ request,
+ MHD_REQUEST_INFO_FIXED_APP_CONTEXT,
+ &fi));
+ bc = *fi.v_ppvoid;
+ if (NULL == bc)
+ {
+ /* first call, setup internals */
+ bc = GNUNET_new (struct BackupContext);
+ bc->request = request;
+ bc->account = *account;
+ GNUNET_assert (MHD_SC_OK ==
+ MHD_REQUEST_SET_OPTIONS (
+ MHD_R_OPTION_TERMINATION_CALLBACK (&cleanup_ctx,
+ bc)));
+ *fi.v_ppvoid = bc;
+ }
+ if (NULL != bc->resp)
+ return MHD_action_from_response (request,
+ bc->resp);
+ if ( (upload_size / 1024 / 1024 >= SH_upload_limit_mb) ||
+ (upload_size > SIZE_MAX) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD2_reply_with_error (
+ request,
+ MHD_HTTP_STATUS_CONTENT_TOO_LARGE,
+ TALER_EC_SYNC_EXCESSIVE_CONTENT_LENGTH,
+ NULL);
+ }
+ {
+ const struct MHD_StringNullable *fresh;
+
+ fresh = MHD_request_get_value (request,
+ MHD_VK_GET_ARGUMENT,
+ "fresh");
+ if (NULL != fresh)
+ bc->force_fresh_order = true;
+ }
+ {
+ const struct MHD_StringNullable *im;
+
+ im = MHD_request_get_value (request,
+ MHD_VK_HEADER,
+ MHD_HTTP_HEADER_IF_MATCH);
+ if (NULL != im)
+ {
+ if ( (2 >= im->len) ||
+ ('"' != im->cstr[0]) ||
+ ('"' != im->cstr[im->len - 1]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (im->cstr + 1,
+ im->len - 2,
+ &bc->old_backup_hash,
+ sizeof (bc->old_backup_hash))) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD2_reply_with_error (
+ request,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ TALER_EC_SYNC_BAD_IF_MATCH,
+ NULL);
+ }
+ }
+ }
+ {
+ const struct MHD_StringNullable *sig_s;
+
+ sig_s = MHD_request_get_value (request,
+ MHD_VK_HEADER,
+ "Sync-Signature");
+ if ( (NULL == sig_s) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (sig_s->cstr,
+ sig_s->len,
+ &bc->account_sig,
+ sizeof (bc->account_sig))) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD2_reply_with_error (
+ request,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ TALER_EC_SYNC_BAD_SYNC_SIGNATURE,
+ NULL);
+ }
+ }
+ {
+ const struct MHD_StringNullable *etag;
+
+ etag = MHD_request_get_value (request,
+ MHD_VK_HEADER,
+ MHD_HTTP_HEADER_IF_NONE_MATCH);
+ if ( (NULL == etag) ||
+ (2 >= etag->len) ||
+ ('"' != etag->cstr[0]) ||
+ ('"' != etag->cstr[etag->len - 1]) ||
+ (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (etag->cstr + 1,
+ etag->len - 2,
+ &bc->new_backup_hash,
+ sizeof (bc->new_backup_hash))) )
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD2_reply_with_error (
+ request,
+ MHD_HTTP_STATUS_BAD_REQUEST,
+ TALER_EC_SYNC_BAD_IF_NONE_MATCH,
+ NULL);
+ }
+ }
+ /* validate signature */
+ {
+ struct SYNC_UploadSignaturePS usp = {
+ .purpose.size = htonl (sizeof (usp)),
+ .purpose.purpose = htonl (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD),
+ .old_backup_hash = bc->old_backup_hash,
+ .new_backup_hash = bc->new_backup_hash
+ };
+
+ if (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_SYNC_BACKUP_UPLOAD,
+ &usp,
+ &bc->account_sig.eddsa_sig,
+ &account->eddsa_pub))
+ {
+ GNUNET_break_op (0);
+ return TALER_MHD2_reply_with_error (
+ request,
+ MHD_HTTP_STATUS_FORBIDDEN,
+ TALER_EC_SYNC_INVALID_SIGNATURE,
+ NULL);
+ }
+ }
+
+ /* Check database to see if the transaction is permissible */
+ {
+ struct GNUNET_HashCode hc;
+ enum SYNC_DB_QueryStatus qs;
+
+ qs = db->lookup_account_TR (db->cls,
+ account,
+ &hc);
+ if (qs < 0)
+ {
+ bool suspend;
+ struct MHD_Response *resp;
+
+ resp = handle_database_error (bc,
+ qs,
+ &suspend);
+ if (suspend)
+ return MHD_action_suspend (request);
+ return MHD_action_from_response (request,
+ resp);
+ }
+ if (SYNC_DB_NO_RESULTS == qs)
+ memset (&hc,
+ 0,
+ sizeof (hc));
+ if (0 == GNUNET_memcmp (&hc,
+ &bc->new_backup_hash))
+ {
+ /* Refuse upload: we already have that backup! */
+ struct MHD_Response *resp;
+
+ resp = MHD_response_from_empty (MHD_HTTP_STATUS_NOT_MODIFIED);
+ TALER_MHD2_add_global_headers (resp);
+ return MHD_action_from_response (request,
+ resp);
+ }
+ if (0 != GNUNET_memcmp (&hc,
+ &bc->old_backup_hash))
+ {
+ /* Refuse upload: if-none-match failed! */
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Conflict detected, returning existing backup\n");
+ return MHD_action_from_response (
+ request,
+ SH_make_backup (account,
+ MHD_HTTP_STATUS_CONFLICT));
+ }
+ }
+ /* check if the client insists on paying */
+ {
+ const struct MHD_StringNullable *order_req;
+
+ order_req = MHD_request_get_value (request,
+ MHD_VK_GET_ARGUMENT,
+ "pay");
+ if (NULL != order_req)
+ {
+ struct MHD_Response *resp;
+ bool suspend;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Payment requested, starting payment process\n");
+ resp = begin_payment (bc,
+ GNUNET_YES,
+ &suspend);
+ if (suspend)
+ return MHD_action_suspend (request);
+ return MHD_action_from_response (request,
+ resp);
+ }
}
+ /* ready to begin upload! */
+ return MHD_action_process_upload_full (request,
+ upload_size,
+ &handle_upload,
+ bc);
}
diff --git a/src/sync/sync-httpd2_backup.c b/src/sync/sync-httpd2_backup.c
@@ -83,21 +83,21 @@ SH_backup_get (struct MHD_Request *request,
}
case SYNC_DB_ONE_RESULT:
{
- const char *inm;
+ const struct MHD_StringNullable *inm;
inm = MHD_request_get_value (request,
MHD_VK_HEADER,
MHD_HTTP_HEADER_IF_NONE_MATCH);
if ( (NULL != inm) &&
- (2 < strlen (inm)) &&
- ('"' == inm[0]) &&
- ('=' == inm[strlen (inm) - 1]) )
+ (2 < inm->len) &&
+ ('"' == inm->cstr[0]) &&
+ ('=' == inm->cstr[inm->len - 1]) )
{
struct GNUNET_HashCode inm_h;
if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (inm + 1,
- strlen (inm) - 2,
+ GNUNET_STRINGS_string_to_data (inm->cstr + 1,
+ inm->len - 2,
&inm_h,
sizeof (inm_h)))
{
@@ -122,9 +122,9 @@ SH_backup_get (struct MHD_Request *request,
/* We have a result, should fetch and return it! */
break;
}
- return SH_return_backup (request,
- account,
- MHD_HTTP_STATUS_OK);
+ return MHD_action_from_response (request,
+ SH_make_backup (account,
+ MHD_HTTP_STATUS_OK));
}
@@ -132,16 +132,14 @@ SH_backup_get (struct MHD_Request *request,
* Return the current backup of @a account on @a request
* using @a default_http_status on success.
*
- * @param request MHD request to use
* @param account account to query
* @param default_http_status HTTP status to queue response
* with on success (#MHD_HTTP_STATUS_OK or #MHD_HTTP_STATUS_CONFLICT)
- * @return MHD action
+ * @return MHD response
*/
-const struct MHD_Action *
-SH_return_backup (struct MHD_Request *request,
- const struct SYNC_AccountPublicKeyP *account,
- enum MHD_HTTP_StatusCode default_http_status)
+struct MHD_Response *
+SH_make_backup (const struct SYNC_AccountPublicKeyP *account,
+ enum MHD_HTTP_StatusCode default_http_status)
{
enum SYNC_DB_QueryStatus qs;
struct MHD_Response *resp;
@@ -162,44 +160,33 @@ SH_return_backup (struct MHD_Request *request,
{
case SYNC_DB_OLD_BACKUP_MISSING:
GNUNET_break (0);
- return TALER_MHD2_reply_with_error (request,
- MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "unexpected return status (backup missing)");
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "unexpected return status (backup missing)");
case SYNC_DB_OLD_BACKUP_MISMATCH:
GNUNET_break (0);
- return TALER_MHD2_reply_with_error (request,
- MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "unexpected return status (backup mismatch)");
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "unexpected return status (backup mismatch)");
case SYNC_DB_PAYMENT_REQUIRED:
GNUNET_break (0);
- return TALER_MHD2_reply_with_error (request,
- MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "unexpected return status (payment required)");
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
+ "unexpected return status (payment required)")
+ ;
case SYNC_DB_HARD_ERROR:
GNUNET_break (0);
- return TALER_MHD2_reply_with_error (request,
- MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_FETCH_FAILED,
- NULL);
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_FETCH_FAILED,
+ NULL);
case SYNC_DB_SOFT_ERROR:
GNUNET_break (0);
- return TALER_MHD2_reply_with_error (request,
- MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_SOFT_FAILURE,
- NULL);
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_SOFT_FAILURE,
+ NULL);
case SYNC_DB_NO_RESULTS:
GNUNET_break (0);
/* Note: can theoretically happen due to non-transactional nature if
the backup expired / was gc'ed JUST between the two SQL calls.
But too rare to handle properly, as doing a transaction would be
expensive. Just admit to failure ;-) */
- return TALER_MHD2_reply_with_error (request,
- MHD_HTTP_STATUS_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
- NULL);
+ return TALER_MHD2_make_error (TALER_EC_GENERIC_DB_INVARIANT_FAILURE,
+ NULL);
case SYNC_DB_ONE_RESULT:
/* interesting case below */
break;
@@ -242,6 +229,5 @@ SH_return_backup (struct MHD_Request *request,
GNUNET_free (prev_s);
GNUNET_free (sig_s);
}
- return MHD_action_from_response (request,
- resp);
+ return resp;
}
diff --git a/src/sync/sync-httpd2_backup.h b/src/sync/sync-httpd2_backup.h
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014, 2015, 2016 GNUnet e.V.
+ Copyright (C) 2014--2025 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
@@ -34,16 +34,14 @@ SH_resume_all_bc (void);
* Return the current backup of @a account on @a request
* using @a default_http_status on success.
*
- * @param request MHD request to use
* @param account account to query
* @param default_http_status HTTP status to queue response
- * with on success (#MHD_HTTP_OK or #MHD_HTTP_CONFLICT)
- * @return MHD action
+ * with on success (#MHD_HTTP_STATUS_OK or #MHD_HTTP_STATUS_CONFLICT)
+ * @return MHD response
*/
-const struct MHD_Action *
-SH_return_backup (struct MHD_Request *request,
- const struct SYNC_AccountPublicKeyP *account,
- unsigned int default_http_status);
+struct MHD_Response *
+SH_make_backup (const struct SYNC_AccountPublicKeyP *account,
+ enum MHD_HTTP_StatusCode default_http_status);
/**