diff options
Diffstat (limited to 'src/backend/anastasis-httpd_policy_upload.c')
-rw-r--r-- | src/backend/anastasis-httpd_policy_upload.c | 1212 |
1 files changed, 0 insertions, 1212 deletions
diff --git a/src/backend/anastasis-httpd_policy_upload.c b/src/backend/anastasis-httpd_policy_upload.c deleted file mode 100644 index c36cc17..0000000 --- a/src/backend/anastasis-httpd_policy_upload.c +++ /dev/null @@ -1,1212 +0,0 @@ -/* - This file is part of Anastasis - Copyright (C) 2021 Anastasis SARL - - Anastasis 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. - - Anastasis 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 - Anastasis; see the file COPYING. If not, see <http://www.gnu.org/licenses/> -*/ -/** - * @file anastasis-httpd_policy.c - * @brief functions to handle incoming requests on /policy/ - * @author Dennis Neufeld - * @author Dominik Meister - * @author Christian Grothoff - */ -#include "platform.h" -#include "anastasis-httpd.h" -#include "anastasis-httpd_policy.h" -#include "anastasis_service.h" -#include <gnunet/gnunet_util_lib.h> -#include <gnunet/gnunet_rest_lib.h> -#include <taler/taler_json_lib.h> -#include <taler/taler_merchant_service.h> -#include <taler/taler_signatures.h> - -/** - * How long do we hold an HTTP client connection if - * we are awaiting payment before giving up? - */ -#define CHECK_PAYMENT_GENERIC_TIMEOUT GNUNET_TIME_relative_multiply ( \ - GNUNET_TIME_UNIT_MINUTES, 30) - - -/** - * Context for an upload operation. - */ -struct PolicyUploadContext -{ - - /** - * Signature of the account holder. - */ - struct ANASTASIS_AccountSignatureP account_sig; - - /** - * Public key of the account holder. - */ - struct ANASTASIS_CRYPTO_AccountPublicKeyP account; - - /** - * Hash of the upload we are receiving right now (as promised - * by the client, to be verified!). - */ - struct GNUNET_HashCode new_policy_upload_hash; - - /** - * Hash context for the upload. - */ - struct GNUNET_HashContext *hash_ctx; - - /** - * Kept in DLL for shutdown handling while suspended. - */ - struct PolicyUploadContext *next; - - /** - * Kept in DLL for shutdown handling while suspended. - */ - struct PolicyUploadContext *prev; - - /** - * Used while suspended for resumption. - */ - struct MHD_Connection *con; - - /** - * Upload, with as many bytes as we have received so far. - */ - char *upload; - - /** - * Used while we are awaiting proposal creation. - */ - struct TALER_MERCHANT_PostOrdersHandle *po; - - /** - * Used while we are waiting payment. - */ - struct TALER_MERCHANT_OrderMerchantGetHandle *cpo; - - /** - * 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; - - /** - * Payment Identifier - */ - struct ANASTASIS_PaymentSecretP payment_identifier; - - /** - * Timestamp of the order in @e payment_identifier. Used to - * select the most recent unpaid offer. - */ - struct GNUNET_TIME_Absolute existing_pi_timestamp; - - /** - * When does the operation timeout? - */ - struct GNUNET_TIME_Absolute timeout; - - /** - * How long must the account be valid? Determines whether we should - * trigger payment, and if so how much. - */ - struct GNUNET_TIME_Absolute end_date; - - /** - * How long is the account already valid? - * Determines how much the user needs to pay. - */ - struct GNUNET_TIME_Absolute paid_until; - - /** - * 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; - - /** - * For how many years does the client still have - * to pay? - */ - unsigned int years_to_pay; - - /** - * true if client provided a payment secret / order ID? - */ - bool payment_identifier_provided; - -}; - - -/** - * Kept in DLL for shutdown handling while suspended. - */ -static struct PolicyUploadContext *puc_head; - -/** - * Kept in DLL for shutdown handling while suspended. - */ -static struct PolicyUploadContext *puc_tail; - - -/** - * Service is shutting down, resume all MHD connections NOW. - */ -void -AH_resume_all_bc () -{ - struct PolicyUploadContext *puc; - - while (NULL != (puc = puc_head)) - { - GNUNET_CONTAINER_DLL_remove (puc_head, - puc_tail, - puc); - if (NULL != puc->po) - { - TALER_MERCHANT_orders_post_cancel (puc->po); - puc->po = NULL; - } - if (NULL != puc->cpo) - { - TALER_MERCHANT_merchant_order_get_cancel (puc->cpo); - puc->cpo = NULL; - } - MHD_resume_connection (puc->con); - } -} - - -/** - * Function called to clean up a backup context. - * - * @param hc a `struct PolicyUploadContext` - */ -static void -cleanup_ctx (struct TM_HandlerContext *hc) -{ - struct PolicyUploadContext *puc = hc->ctx; - - if (NULL != puc->po) - TALER_MERCHANT_orders_post_cancel (puc->po); - if (NULL != puc->cpo) - TALER_MERCHANT_merchant_order_get_cancel (puc->cpo); - if (NULL != puc->hash_ctx) - GNUNET_CRYPTO_hash_context_abort (puc->hash_ctx); - if (NULL != puc->resp) - MHD_destroy_response (puc->resp); - GNUNET_free (puc->upload); - GNUNET_free (puc); -} - - -/** - * Transmit a payment request for @a order_id on @a connection - * - * @param[in,out] puc details about the operation - * @return #GNUNET_OK on success - */ -static int -make_payment_request (struct PolicyUploadContext *puc) -{ - struct MHD_Response *resp; - - /* request payment via Taler */ - resp = MHD_create_response_from_buffer (0, - NULL, - MHD_RESPMEM_PERSISTENT); - if (NULL == resp) - { - GNUNET_break (0); - return GNUNET_SYSERR; - } - TALER_MHD_add_global_headers (resp); - { - char *hdr; - char *pfx; - char *hn; - - if (0 == strncasecmp ("https://", - AH_backend_url, - strlen ("https://"))) - { - pfx = "taler://"; - hn = &AH_backend_url[strlen ("https://")]; - } - else if (0 == strncasecmp ("http://", - AH_backend_url, - strlen ("http://"))) - { - pfx = "taler+http://"; - hn = &AH_backend_url[strlen ("http://")]; - } - else - { - GNUNET_break (0); - MHD_destroy_response (resp); - return GNUNET_SYSERR; - } - if (0 == strlen (hn)) - { - GNUNET_break (0); - MHD_destroy_response (resp); - return GNUNET_SYSERR; - } - { - char *order_id; - - order_id = GNUNET_STRINGS_data_to_string_alloc ( - &puc->payment_identifier, - sizeof (puc->payment_identifier)); - GNUNET_asprintf (&hdr, - "%spay/%s%s/", - pfx, - hn, - order_id); - GNUNET_free (order_id); - } - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - ANASTASIS_HTTP_HEADER_TALER, - hdr)); - GNUNET_free (hdr); - } - puc->resp = resp; - puc->response_code = MHD_HTTP_PAYMENT_REQUIRED; - return GNUNET_OK; -} - - -/** - * Callbacks of this type are used to serve the result of submitting a - * POST /private/orders request to a merchant. - * - * @param cls our `struct PolicyUploadContext` - * @param por response details - */ -static void -proposal_cb (void *cls, - const struct TALER_MERCHANT_PostOrdersReply *por) -{ - struct PolicyUploadContext *puc = cls; - enum GNUNET_DB_QueryStatus qs; - - puc->po = NULL; - GNUNET_CONTAINER_DLL_remove (puc_head, - puc_tail, - puc); - MHD_resume_connection (puc->con); - AH_trigger_daemon (NULL); - if (MHD_HTTP_OK != por->hr.http_status) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Backend returned status %u/%d\n", - por->hr.http_status, - (int) por->hr.ec); - GNUNET_break (0); - puc->resp = TALER_MHD_MAKE_JSON_PACK ( - GNUNET_JSON_pack_uint64 ("code", - TALER_EC_SYNC_PAYMENT_CREATE_BACKEND_ERROR), - GNUNET_JSON_pack_string ("hint", - "Failed to setup order with merchant backend"), - GNUNET_JSON_pack_uint64 ("backend-ec", - por->hr.ec), - GNUNET_JSON_pack_uint64 ("backend-http-status", - por->hr.http_status), - GNUNET_JSON_pack_allow_null ( - GNUNET_JSON_pack_object_incref ("backend-reply", - (json_t *) por->hr.reply))); - puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Storing payment request for order `%s'\n", - por->details.ok.order_id); - - qs = db->record_recdoc_payment (db->cls, - &puc->account, - (uint32_t) AH_post_counter, - &puc->payment_identifier, - &AH_annual_fee); - if (0 >= qs) - { - GNUNET_break (0); - puc->resp = TALER_MHD_make_error ( - TALER_EC_GENERIC_DB_STORE_FAILED, - "record recdoc payment"); - puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - return; - } - if (GNUNET_OK != - make_payment_request (puc)) - { - GNUNET_break (0); - puc->resp = TALER_MHD_make_error ( - TALER_EC_GENERIC_DB_STORE_FAILED, - "failed to initiate payment"); - puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - } -} - - -/** - * Callback to process a GET /check-payment request - * - * @param cls our `struct PolicyUploadContext` - * @param hr HTTP response details - * @param osr order status - */ -static void -check_payment_cb (void *cls, - const struct TALER_MERCHANT_HttpResponse *hr, - const struct TALER_MERCHANT_OrderStatusResponse *osr) -{ - struct PolicyUploadContext *puc = cls; - - /* refunds are not supported, verify */ - puc->cpo = NULL; - GNUNET_CONTAINER_DLL_remove (puc_head, - puc_tail, - puc); - MHD_resume_connection (puc->con); - AH_trigger_daemon (NULL); - switch (hr->http_status) - { - case MHD_HTTP_OK: - GNUNET_assert (NULL != osr); - break; /* processed below */ - case MHD_HTTP_UNAUTHORIZED: - puc->resp = TALER_MHD_make_error ( - TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED, - NULL); - puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - return; - default: - puc->resp = TALER_MHD_make_error ( - TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR, - "failed to initiate payment"); - puc->response_code = MHD_HTTP_BAD_GATEWAY; - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Payment status checked: %s\n", - osr->status ? "paid" : "unpaid"); - switch (osr->status) - { - case TALER_MERCHANT_OSC_PAID: - { - enum GNUNET_DB_QueryStatus qs; - unsigned int years; - struct GNUNET_TIME_Relative paid_until; - const json_t *contract; - struct TALER_Amount amount; - struct GNUNET_JSON_Specification cspec[] = { - TALER_JSON_spec_amount ("amount", - AH_currency, - &amount), - GNUNET_JSON_spec_end () - }; - - contract = osr->details.paid.contract_terms; - if (GNUNET_OK != - GNUNET_JSON_parse (contract, - cspec, - NULL, NULL)) - { - GNUNET_break (0); - puc->resp = TALER_MHD_make_error ( - TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, - "no amount given"); - puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - return; /* continue as planned */ - } - years = TALER_amount_divide2 (&amount, - &AH_annual_fee); - paid_until = GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, - years); - /* add 1 week grace period, otherwise if a user - wants to pay for 1 year, the first seconds - would have passed between making the payment - and our subsequent check if +1 year was - paid... So we actually say 1 year = 52 weeks - on the server, while the client calculates - with 365 days. */ - paid_until = GNUNET_TIME_relative_add (paid_until, - GNUNET_TIME_UNIT_WEEKS); - - qs = db->increment_lifetime (db->cls, - &puc->account, - &puc->payment_identifier, - paid_until, - &puc->paid_until); - if (0 <= qs) - return; /* continue as planned */ - GNUNET_break (0); - puc->resp = TALER_MHD_make_error ( - TALER_EC_GENERIC_DB_FETCH_FAILED, - "increment lifetime"); - puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - return; /* continue as planned */ - } - case TALER_MERCHANT_OSC_UNPAID: - case TALER_MERCHANT_OSC_CLAIMED: - break; - } - if (0 != puc->existing_pi_timestamp.abs_value_us) - { - /* repeat payment request */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Repeating payment request\n"); - if (GNUNET_OK != - make_payment_request (puc)) - { - GNUNET_break (0); - puc->resp = TALER_MHD_make_error ( - TALER_EC_GENERIC_DB_STORE_FAILED, - "failed to initiate payment"); - puc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; - } - return; - } - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Timeout waiting for payment\n"); - puc->resp = TALER_MHD_make_error (TALER_EC_SYNC_PAYMENT_GENERIC_TIMEOUT, - "Timeout awaiting promised payment"); - GNUNET_assert (NULL != puc->resp); - puc->response_code = MHD_HTTP_REQUEST_TIMEOUT; -} - - -/** - * Helper function used to ask our backend to await - * a payment for the user's account. - * - * @param puc context to begin payment for. - */ -static void -await_payment (struct PolicyUploadContext *puc) -{ - struct GNUNET_TIME_Relative timeout - = GNUNET_TIME_absolute_get_remaining (puc->timeout); - - GNUNET_CONTAINER_DLL_insert (puc_head, - puc_tail, - puc); - MHD_suspend_connection (puc->con); - { - char *order_id; - - order_id = GNUNET_STRINGS_data_to_string_alloc ( - &puc->payment_identifier, - sizeof(struct ANASTASIS_PaymentSecretP)); - puc->cpo = TALER_MERCHANT_merchant_order_get (AH_ctx, - AH_backend_url, - order_id, - NULL /* our payments are NOT session-bound */, - false, - timeout, - &check_payment_cb, - puc); - GNUNET_free (order_id); - } - AH_trigger_curl (); -} - - -/** - * 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 if required. - * - * @param puc context to begin payment for. - * @return MHD status code - */ -static MHD_RESULT -begin_payment (struct PolicyUploadContext *puc) -{ - json_t *order; - - GNUNET_CONTAINER_DLL_insert (puc_head, - puc_tail, - puc); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Suspending connection while creating order at `%s'\n", - AH_backend_url); - { - char *order_id; - struct TALER_Amount upload_fee; - - if (0 > - TALER_amount_multiply (&upload_fee, - &AH_annual_fee, - puc->years_to_pay)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (puc->con, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "storage_duration_years"); - } - - order_id = GNUNET_STRINGS_data_to_string_alloc ( - &puc->payment_identifier, - sizeof(struct ANASTASIS_PaymentSecretP)); - order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s }", - "amount", TALER_JSON_from_amount (&upload_fee), - "summary", "Anastasis policy storage fee", - "products", - "description", "policy storage fee", - "quantity", (json_int_t) puc->years_to_pay, - "unit", "years", - "order_id", order_id); - GNUNET_free (order_id); - } - MHD_suspend_connection (puc->con); - puc->po = TALER_MERCHANT_orders_post2 (AH_ctx, - AH_backend_url, - order, - GNUNET_TIME_UNIT_ZERO, - NULL, /* no payment target */ - 0, - NULL, /* no inventory products */ - 0, - NULL, /* no uuids */ - false, /* do NOT require claim token */ - &proposal_cb, - puc); - AH_trigger_curl (); - json_decref (order); - return MHD_YES; -} - - -/** - * Prepare to receive a payment, possibly requesting it, or just waiting - * for it to be completed by the client. - * - * @param puc context to prepare payment for - * @return MHD status - */ -static MHD_RESULT -prepare_payment (struct PolicyUploadContext *puc) -{ - if (! puc->payment_identifier_provided) - { - GNUNET_CRYPTO_random_block ( - GNUNET_CRYPTO_QUALITY_NONCE, - &puc->payment_identifier, - sizeof (struct ANASTASIS_PaymentSecretP)); - puc->payment_identifier_provided = true; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No payment identifier, initiating payment\n"); - return begin_payment (puc); - } - await_payment (puc); - return MHD_YES; -} - - -MHD_RESULT -AH_handler_policy_post ( - struct MHD_Connection *connection, - struct TM_HandlerContext *hc, - const struct ANASTASIS_CRYPTO_AccountPublicKeyP *account_pub, - const char *recovery_data, - size_t *recovery_data_size) -{ - struct PolicyUploadContext *puc = hc->ctx; - - if (NULL == puc) - { - /* first call, setup internals */ - puc = GNUNET_new (struct PolicyUploadContext); - hc->ctx = puc; - hc->cc = &cleanup_ctx; - puc->con = connection; - - { - const char *pay_id; - - pay_id = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER); - if (NULL != pay_id) - { - if (GNUNET_OK != - GNUNET_STRINGS_string_to_data ( - pay_id, - strlen (pay_id), - &puc->payment_identifier, - sizeof (struct ANASTASIS_PaymentSecretP))) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - ANASTASIS_HTTP_HEADER_PAYMENT_IDENTIFIER - " header must be a base32-encoded Payment-Secret"); - } - puc->payment_identifier_provided = true; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Policy upload started with payment identifier `%s'\n", - pay_id); - } - } - puc->account = *account_pub; - /* now setup 'puc' */ - { - const char *lens; - unsigned long len; - char dummy; - - lens = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_CONTENT_LENGTH); - if ( (NULL == lens) || - (1 != sscanf (lens, - "%lu%c", - &len, - &dummy)) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error ( - connection, - MHD_HTTP_BAD_REQUEST, - (NULL == lens) - ? TALER_EC_ANASTASIS_GENERIC_MISSING_CONTENT_LENGTH - : TALER_EC_ANASTASIS_GENERIC_MALFORMED_CONTENT_LENGTH, - NULL); - } - if (len / 1024 / 1024 >= AH_upload_limit_mb) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_PAYLOAD_TOO_LARGE, - TALER_EC_SYNC_MALFORMED_CONTENT_LENGTH, - "Content-length value not acceptable"); - } - puc->upload = GNUNET_malloc_large (len); - if (NULL == puc->upload) - { - GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, - "malloc"); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_PAYLOAD_TOO_LARGE, - TALER_EC_ANASTASIS_POLICY_OUT_OF_MEMORY_ON_CONTENT_LENGTH, - NULL); - } - puc->upload_size = (size_t) len; - } - { - /* Check if header contains Anastasis-Policy-Signature */ - const char *sig_s; - - sig_s = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE); - if ( (NULL == sig_s) || - (GNUNET_OK != - GNUNET_STRINGS_string_to_data (sig_s, - strlen (sig_s), - &puc->account_sig, - sizeof (puc->account_sig))) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE, - ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE - " header must include a base32-encoded EdDSA signature"); - } - } - { - /* Check if header contains an ETAG */ - const char *etag; - - etag = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_IF_NONE_MATCH); - if ( (NULL == etag) || - (GNUNET_OK != - GNUNET_STRINGS_string_to_data (etag, - strlen (etag), - &puc->new_policy_upload_hash, - sizeof (puc->new_policy_upload_hash))) ) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_ANASTASIS_POLICY_BAD_IF_MATCH, - MHD_HTTP_HEADER_IF_NONE_MATCH - " header must include a base32-encoded SHA-512 hash"); - } - } - /* validate signature */ - { - struct ANASTASIS_UploadSignaturePS usp = { - .purpose.size = htonl (sizeof (usp)), - .purpose.purpose = htonl (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD), - .new_recovery_data_hash = puc->new_policy_upload_hash - }; - - if (GNUNET_OK != - GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_ANASTASIS_POLICY_UPLOAD, - &usp, - &puc->account_sig.eddsa_sig, - &account_pub->pub)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_FORBIDDEN, - TALER_EC_ANASTASIS_POLICY_BAD_SIGNATURE, - ANASTASIS_HTTP_HEADER_POLICY_SIGNATURE); - } - } - - { - const char *long_poll_timeout_ms; - - long_poll_timeout_ms = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "timeout_ms"); - if (NULL != long_poll_timeout_ms) - { - unsigned int timeout; - char dummy; - - if (1 != sscanf (long_poll_timeout_ms, - "%u%c", - &timeout, - &dummy)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "timeout_ms (must be non-negative number)"); - } - puc->timeout - = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_MILLISECONDS, - timeout)); - } - else - { - puc->timeout = GNUNET_TIME_relative_to_absolute - (CHECK_PAYMENT_GENERIC_TIMEOUT); - } - } - - /* check if the client insists on paying */ - { - const char *req; - unsigned int years; - - req = MHD_lookup_connection_value (connection, - MHD_GET_ARGUMENT_KIND, - "storage_duration"); - if (NULL != req) - { - char dummy; - - if (1 != sscanf (req, - "%u%c", - &years, - &dummy)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_GENERIC_PARAMETER_MALFORMED, - "storage_duration (must be non-negative number)"); - } - } - else - { - years = 0; - } - puc->end_date = GNUNET_TIME_relative_to_absolute ( - GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, - years)); - } - - /* get ready to hash (done here as we may go async for payments next) */ - puc->hash_ctx = GNUNET_CRYPTO_hash_context_start (); - - /* Check database to see if the transaction is permissible */ - { - struct GNUNET_TIME_Relative rem; - - rem = GNUNET_TIME_absolute_get_remaining (puc->end_date); - puc->years_to_pay = rem.rel_value_us - / GNUNET_TIME_UNIT_YEARS.rel_value_us; - if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) - puc->years_to_pay++; - - if (puc->payment_identifier_provided) - { - /* check if payment identifier is valid (existing and paid) */ - bool paid; - bool valid_counter; - enum GNUNET_DB_QueryStatus qs; - - qs = db->check_payment_identifier (db->cls, - &puc->payment_identifier, - &paid, - &valid_counter); - if (qs < 0) - return TALER_MHD_reply_with_error (puc->con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - - if ( (! paid) || - (! valid_counter) ) - { - if (! valid_counter) - { - puc->payment_identifier_provided = false; - if (0 == puc->years_to_pay) - puc->years_to_pay = 1; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Too many uploads with this payment identifier, initiating fresh payment\n"); - } - else - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Given payment identifier not known to be paid, initiating payment\n"); - } - return prepare_payment (puc); - } - } - - if (! puc->payment_identifier_provided) - { - struct TALER_Amount zero_amount; - enum GNUNET_DB_QueryStatus qs; - struct GNUNET_TIME_Relative rel; - - TALER_amount_set_zero (AH_currency, - &zero_amount); - /* generate fresh payment identifier */ - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, - &puc->payment_identifier, - sizeof (struct ANASTASIS_PaymentSecretP)); - if (0 != TALER_amount_cmp (&AH_annual_fee, - &zero_amount)) - { - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No payment identifier, requesting payment\n"); - return begin_payment (puc); - } - /* Cost is zero, fake "zero" payment having happened */ - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Policy upload is free, allowing upload without payment\n"); - qs = db->record_recdoc_payment (db->cls, - account_pub, - AH_post_counter, - &puc->payment_identifier, - &AH_annual_fee); - if (qs <= 0) - return TALER_MHD_reply_with_error (puc->con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - rel = GNUNET_TIME_relative_multiply ( - GNUNET_TIME_UNIT_YEARS, - ANASTASIS_MAX_YEARS_STORAGE); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Policy lifetime is %s (%u years)\n", - GNUNET_STRINGS_relative_time_to_string (rel, - GNUNET_YES), - ANASTASIS_MAX_YEARS_STORAGE); - puc->paid_until = GNUNET_TIME_relative_to_absolute (rel); - qs = db->update_lifetime (db->cls, - account_pub, - &puc->payment_identifier, - puc->paid_until); - if (qs <= 0) - { - GNUNET_break (0); - return TALER_MHD_reply_with_error (puc->con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - } - } - } - - /* Check if existing policy matches upload (and if, skip it) */ - { - struct GNUNET_HashCode hc; - enum ANASTASIS_DB_AccountStatus as; - uint32_t version; - struct GNUNET_TIME_Absolute now; - struct GNUNET_TIME_Relative rem; - - as = db->lookup_account (db->cls, - account_pub, - &puc->paid_until, - &hc, - &version); - now = GNUNET_TIME_absolute_get (); - if (puc->paid_until.abs_value_us < now.abs_value_us) - puc->paid_until = now; - rem = GNUNET_TIME_absolute_get_difference (puc->paid_until, - puc->end_date); - puc->years_to_pay = rem.rel_value_us - / GNUNET_TIME_UNIT_YEARS.rel_value_us; - if (0 != (rem.rel_value_us % GNUNET_TIME_UNIT_YEARS.rel_value_us)) - puc->years_to_pay++; - - if ( (ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED == as) && - (0 != puc->years_to_pay) ) - { - /* user requested extension, force payment */ - as = ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED; - } - switch (as) - { - case ANASTASIS_DB_ACCOUNT_STATUS_PAYMENT_REQUIRED: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Expiration too low, initiating payment\n"); - return prepare_payment (puc); - case ANASTASIS_DB_ACCOUNT_STATUS_HARD_ERROR: - return TALER_MHD_reply_with_error (puc->con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case ANASTASIS_DB_ACCOUNT_STATUS_NO_RESULTS: - /* continue below */ - break; - case ANASTASIS_DB_ACCOUNT_STATUS_VALID_HASH_RETURNED: - if (0 == GNUNET_memcmp (&hc, - &puc->new_policy_upload_hash)) - { - /* Refuse upload: we already have that backup! */ - struct MHD_Response *resp; - MHD_RESULT ret; - char version_s[14]; - - GNUNET_snprintf (version_s, - sizeof (version_s), - "%u", - (unsigned int) version); - resp = MHD_create_response_from_buffer (0, - NULL, - MHD_RESPMEM_PERSISTENT); - TALER_MHD_add_global_headers (resp); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - ANASTASIS_HTTP_HEADER_POLICY_VERSION, - version_s)); - ret = MHD_queue_response (connection, - MHD_HTTP_NOT_MODIFIED, - resp); - GNUNET_break (MHD_YES == ret); - MHD_destroy_response (resp); - return ret; - } - break; - } - } - /* ready to begin! */ - return MHD_YES; - } - - if (NULL != puc->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", - puc->response_code); - ret = MHD_queue_response (connection, - puc->response_code, - puc->resp); - GNUNET_break (MHD_YES == ret); - MHD_destroy_response (puc->resp); - puc->resp = NULL; - return ret; - } - - /* handle upload */ - if (0 != *recovery_data_size) - { - /* check MHD invariant */ - GNUNET_assert (puc->upload_off + *recovery_data_size <= puc->upload_size); - memcpy (&puc->upload[puc->upload_off], - recovery_data, - *recovery_data_size); - puc->upload_off += *recovery_data_size; - GNUNET_CRYPTO_hash_context_read (puc->hash_ctx, - recovery_data, - *recovery_data_size); - *recovery_data_size = 0; - return MHD_YES; - } - - if ( (0 == puc->upload_off) && - (0 != puc->upload_size) && - (NULL == puc->resp) ) - { - /* wait for upload */ - return MHD_YES; - } - - /* finished with upload, check hash */ - if (NULL != puc->hash_ctx) - { - struct GNUNET_HashCode our_hash; - - GNUNET_CRYPTO_hash_context_finish (puc->hash_ctx, - &our_hash); - puc->hash_ctx = NULL; - if (0 != GNUNET_memcmp (&our_hash, - &puc->new_policy_upload_hash)) - { - GNUNET_break_op (0); - return TALER_MHD_reply_with_error (connection, - MHD_HTTP_BAD_REQUEST, - TALER_EC_ANASTASIS_POLICY_INVALID_UPLOAD, - "Data uploaded does not match Etag promise"); - } - } - - /* store backup to database */ - { - enum ANASTASIS_DB_StoreStatus ss; - uint32_t version = UINT32_MAX; - char version_s[14]; - char expir_s[32]; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Uploading recovery document\n"); - ss = db->store_recovery_document (db->cls, - &puc->account, - &puc->account_sig, - &puc->new_policy_upload_hash, - puc->upload, - puc->upload_size, - &puc->payment_identifier, - &version); - GNUNET_snprintf (version_s, - sizeof (version_s), - "%u", - (unsigned int) version); - GNUNET_snprintf (expir_s, - sizeof (expir_s), - "%llu", - (unsigned long long) - (puc->paid_until.abs_value_us - / GNUNET_TIME_UNIT_SECONDS.rel_value_us)); - switch (ss) - { - case ANASTASIS_DB_STORE_STATUS_STORE_LIMIT_EXCEEDED: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Storage request limit exceeded, requesting payment\n"); - if (! puc->payment_identifier_provided) - { - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, - &puc->payment_identifier, - sizeof (struct ANASTASIS_PaymentSecretP)); - puc->payment_identifier_provided = true; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Also no payment identifier, requesting payment\n"); - } - return begin_payment (puc); - case ANASTASIS_DB_STORE_STATUS_PAYMENT_REQUIRED: - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Policy store operation requires payment\n"); - if (! puc->payment_identifier_provided) - { - GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_STRONG, - &puc->payment_identifier, - sizeof (struct ANASTASIS_PaymentSecretP)); - puc->payment_identifier_provided = true; - } - return begin_payment (puc); - case ANASTASIS_DB_STORE_STATUS_HARD_ERROR: - case ANASTASIS_DB_STORE_STATUS_SOFT_ERROR: - return TALER_MHD_reply_with_error (puc->con, - MHD_HTTP_INTERNAL_SERVER_ERROR, - TALER_EC_GENERIC_DB_FETCH_FAILED, - NULL); - case ANASTASIS_DB_STORE_STATUS_NO_RESULTS: - { - /* 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); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - "Anastasis-Version", - version_s)); - ret = MHD_queue_response (connection, - MHD_HTTP_NOT_MODIFIED, - resp); - GNUNET_break (MHD_YES == ret); - MHD_destroy_response (resp); - return ret; - } - case ANASTASIS_DB_STORE_STATUS_SUCCESS: - /* 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); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - ANASTASIS_HTTP_HEADER_POLICY_VERSION, - version_s)); - GNUNET_break (MHD_YES == - MHD_add_response_header (resp, - ANASTASIS_HTTP_HEADER_POLICY_EXPIRATION, - expir_s)); - ret = MHD_queue_response (connection, - MHD_HTTP_NO_CONTENT, - resp); - GNUNET_break (MHD_YES == ret); - MHD_destroy_response (resp); - return ret; - } - } - } - GNUNET_break (0); - return MHD_NO; -} |