diff options
author | Christian Grothoff <christian@grothoff.org> | 2021-07-30 10:38:27 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2021-07-30 10:38:27 +0200 |
commit | 7e669bcf6b6336ec429da949bcb4aa456971dba2 (patch) | |
tree | d19912f950d1cac1c38b857b7d5bdaba2289544e /src/backend/anastasis-httpd_truth_upload.c | |
download | anastasis-7e669bcf6b6336ec429da949bcb4aa456971dba2.tar.gz anastasis-7e669bcf6b6336ec429da949bcb4aa456971dba2.tar.bz2 anastasis-7e669bcf6b6336ec429da949bcb4aa456971dba2.zip |
folding history in preparation of GNU Anastasis v0.0.0 release
Diffstat (limited to 'src/backend/anastasis-httpd_truth_upload.c')
-rw-r--r-- | src/backend/anastasis-httpd_truth_upload.c | 855 |
1 files changed, 855 insertions, 0 deletions
diff --git a/src/backend/anastasis-httpd_truth_upload.c b/src/backend/anastasis-httpd_truth_upload.c new file mode 100644 index 0000000..9767087 --- /dev/null +++ b/src/backend/anastasis-httpd_truth_upload.c @@ -0,0 +1,855 @@ +/* + This file is part of TALER + Copyright (C) 2019, 2021 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 + Foundation; either version 3, or (at your option) any later version. + + TALER 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 + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file anastasis-httpd_truth_upload.c + * @brief functions to handle incoming POST request on /truth + * @author Dennis Neufeld + * @author Dominik Meister + * @author Christian Grothoff + */ +#include "platform.h" +#include "anastasis-httpd.h" +#include "anastasis_service.h" +#include "anastasis-httpd_truth.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> +#include "anastasis_authorization_lib.h" + + +/** + * Information we track per truth upload. + */ +struct TruthUploadContext +{ + + /** + * UUID of the truth object we are processing. + */ + struct ANASTASIS_CRYPTO_TruthUUIDP truth_uuid; + + /** + * Kept in DLL for shutdown handling while suspended. + */ + struct TruthUploadContext *next; + + /** + * Kept in DLL for shutdown handling while suspended. + */ + struct TruthUploadContext *prev; + + /** + * Used while we are awaiting proposal creation. + */ + struct TALER_MERCHANT_PostOrdersHandle *po; + + /** + * Used while we are waiting payment. + */ + struct TALER_MERCHANT_OrderMerchantGetHandle *cpo; + + /** + * Post parser context. + */ + void *post_ctx; + + /** + * Handle to the client request. + */ + struct MHD_Connection *connection; + + /** + * Incoming JSON, NULL if not yet available. + */ + json_t *json; + + /** + * HTTP response code to use on resume, if non-NULL. + */ + struct MHD_Response *resp; + + /** + * When should this request time out? + */ + struct GNUNET_TIME_Absolute timeout; + + /** + * Fee that is to be paid for this upload. + */ + struct TALER_Amount upload_fee; + + /** + * HTTP response code to use on resume, if resp is set. + */ + unsigned int response_code; + + /** + * For how many years must the customer still pay? + */ + unsigned int years_to_pay; + +}; + + +/** + * Head of linked list over all truth upload processes + */ +static struct TruthUploadContext *tuc_head; + +/** + * Tail of linked list over all truth upload processes + */ +static struct TruthUploadContext *tuc_tail; + + +void +AH_truth_upload_shutdown (void) +{ + struct TruthUploadContext *tuc; + + while (NULL != (tuc = tuc_head)) + { + GNUNET_CONTAINER_DLL_remove (tuc_head, + tuc_tail, + tuc); + if (NULL != tuc->cpo) + { + TALER_MERCHANT_merchant_order_get_cancel (tuc->cpo); + tuc->cpo = NULL; + } + if (NULL != tuc->po) + { + TALER_MERCHANT_orders_post_cancel (tuc->po); + tuc->po = NULL; + } + MHD_resume_connection (tuc->connection); + } +} + + +/** + * Function called to clean up a `struct TruthUploadContext`. + * + * @param hc general handler context + */ +static void +cleanup_truth_post (struct TM_HandlerContext *hc) +{ + struct TruthUploadContext *tuc = hc->ctx; + + TALER_MHD_parse_post_cleanup_callback (tuc->post_ctx); + if (NULL != tuc->po) + TALER_MERCHANT_orders_post_cancel (tuc->po); + if (NULL != tuc->cpo) + TALER_MERCHANT_merchant_order_get_cancel (tuc->cpo); + if (NULL != tuc->resp) + MHD_destroy_response (tuc->resp); + if (NULL != tuc->json) + json_decref (tuc->json); + GNUNET_free (tuc); +} + + +/** + * Transmit a payment request for @a tuc. + * + * @param tuc upload context to generate payment request for + */ +static void +make_payment_request (struct TruthUploadContext *tuc) +{ + struct MHD_Response *resp; + + /* request payment via Taler */ + resp = MHD_create_response_from_buffer (0, + NULL, + MHD_RESPMEM_PERSISTENT); + GNUNET_assert (NULL != resp); + TALER_MHD_add_global_headers (resp); + { + char *hdr; + const char *pfx; + const 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 + { + /* This invariant holds as per check in anastasis-httpd.c */ + GNUNET_assert (0); + } + /* This invariant holds as per check in anastasis-httpd.c */ + GNUNET_assert (0 != strlen (hn)); + { + char *order_id; + + order_id = GNUNET_STRINGS_data_to_string_alloc ( + &tuc->truth_uuid, + sizeof (tuc->truth_uuid)); + GNUNET_asprintf (&hdr, + "%spay/%s%s/", + pfx, + hn, + order_id); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Returning %u %s\n", + MHD_HTTP_PAYMENT_REQUIRED, + order_id); + GNUNET_free (order_id); + } + GNUNET_break (MHD_YES == + MHD_add_response_header (resp, + ANASTASIS_HTTP_HEADER_TALER, + hdr)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "TRUTH payment request made: %s\n", + hdr); + GNUNET_free (hdr); + } + tuc->resp = resp; + tuc->response_code = MHD_HTTP_PAYMENT_REQUIRED; +} + + +/** + * Callbacks of this type are used to serve the result of submitting a + * POST /private/orders request to a merchant. + * + * @param cls our `struct TruthUploadContext` + * @param por response details + */ +static void +proposal_cb (void *cls, + const struct TALER_MERCHANT_PostOrdersReply *por) +{ + struct TruthUploadContext *tuc = cls; + + tuc->po = NULL; + GNUNET_CONTAINER_DLL_remove (tuc_head, + tuc_tail, + tuc); + MHD_resume_connection (tuc->connection); + 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); + tuc->resp = TALER_MHD_make_json_pack ( + "{s:I, s:s, s:I, s:I, s:O?}", + "code", + (json_int_t) TALER_EC_ANASTASIS_GENERIC_ORDER_CREATE_BACKEND_ERROR, + "hint", + "Failed to setup order with merchant backend", + "backend-ec", + (json_int_t) por->hr.ec, + "backend-http-status", + (json_int_t) por->hr.http_status, + "backend-reply", + por->hr.reply); + GNUNET_assert (NULL != tuc->resp); + tuc->response_code = MHD_HTTP_BAD_GATEWAY; + return; + } + make_payment_request (tuc); +} + + +/** + * 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 TruthUploadContext *tuc = cls; + + tuc->cpo = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Checking backend order status returned %u\n", + hr->http_status); + switch (hr->http_status) + { + case 0: + /* Likely timeout, complain! */ + tuc->response_code = MHD_HTTP_GATEWAY_TIMEOUT; + tuc->resp = TALER_MHD_make_error ( + TALER_EC_ANASTASIS_GENERIC_BACKEND_TIMEOUT, + NULL); + break; + case MHD_HTTP_OK: + 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); + tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + tuc->resp = TALER_MHD_make_error ( + TALER_EC_MERCHANT_GENERIC_DB_CONTRACT_CONTENT_INVALID, + "contract terms in database are malformed"); + break; + } + years = TALER_amount_divide2 (&amount, + &AH_truth_upload_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->record_truth_upload_payment ( + db->cls, + &tuc->truth_uuid, + &osr->details.paid.deposit_total, + paid_until); + if (qs <= 0) + { + GNUNET_break (0); + tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + tuc->resp = TALER_MHD_make_error ( + TALER_EC_GENERIC_DB_STORE_FAILED, + "record_truth_upload_payment"); + break; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Payment confirmed, resuming upload\n"); + break; + case TALER_MERCHANT_OSC_UNPAID: + case TALER_MERCHANT_OSC_CLAIMED: + make_payment_request (tuc); + break; + } + break; + case MHD_HTTP_UNAUTHORIZED: + /* Configuration issue, complain! */ + tuc->response_code = MHD_HTTP_INTERNAL_SERVER_ERROR; + tuc->resp = TALER_MHD_make_json_pack ( + "{s:I, s:s, s:I, s:I, s:O?}", + "code", + (json_int_t) TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED, + "hint", + TALER_ErrorCode_get_hint ( + TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_UNAUTHORIZED), + "backend-ec", + (json_int_t) hr->ec, + "backend-http-status", + (json_int_t) hr->http_status, + "backend-reply", + hr->reply); + GNUNET_assert (NULL != tuc->resp); + break; + case MHD_HTTP_NOT_FOUND: + /* Setup fresh order */ + { + char *order_id; + json_t *order; + + order_id = GNUNET_STRINGS_data_to_string_alloc ( + &tuc->truth_uuid, + sizeof(tuc->truth_uuid)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "%u, setting up fresh order %s\n", + MHD_HTTP_NOT_FOUND, + order_id); + order = json_pack ("{s:o, s:s, s:[{s:s,s:I,s:s}], s:s}", + "amount", + TALER_JSON_from_amount (&tuc->upload_fee), + "summary", + "Anastasis challenge storage fee", + "products", + "description", "challenge storage fee", + "quantity", (json_int_t) tuc->years_to_pay, + "unit", "years", + + "order_id", + order_id); + GNUNET_free (order_id); + tuc->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, + tuc); + AH_trigger_curl (); + json_decref (order); + return; + } + default: + /* Unexpected backend response */ + tuc->response_code = MHD_HTTP_BAD_GATEWAY; + tuc->resp = TALER_MHD_make_json_pack ( + "{s:I, s:s, s:I, s:I, s:O?}", + "code", + (json_int_t) TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR, + "hint", + TALER_ErrorCode_get_hint (TALER_EC_ANASTASIS_GENERIC_BACKEND_ERROR), + "backend-ec", + (json_int_t) hr->ec, + "backend-http-status", + (json_int_t) hr->http_status, + "backend-reply", + hr->reply); + break; + } + GNUNET_CONTAINER_DLL_remove (tuc_head, + tuc_tail, + tuc); + MHD_resume_connection (tuc->connection); + AH_trigger_daemon (NULL); +} + + +/** + * Helper function used to ask our backend to begin processing a + * payment for the truth upload. May perform asynchronous operations + * by suspending the connection if required. + * + * @param tuc context to begin payment for. + * @return MHD status code + */ +static MHD_RESULT +begin_payment (struct TruthUploadContext *tuc) +{ + char *order_id; + struct GNUNET_TIME_Relative timeout; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Checking backend order status...\n"); + timeout = GNUNET_TIME_absolute_get_remaining (tuc->timeout); + order_id = GNUNET_STRINGS_data_to_string_alloc ( + &tuc->truth_uuid, + sizeof (tuc->truth_uuid)); + tuc->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, + tuc); + GNUNET_free (order_id); + if (NULL == tuc->cpo) + { + GNUNET_break (0); + return TALER_MHD_reply_with_error (tuc->connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_ANASTASIS_GENERIC_PAYMENT_CHECK_START_FAILED, + "Could not check order status"); + } + GNUNET_CONTAINER_DLL_insert (tuc_head, + tuc_tail, + tuc); + MHD_suspend_connection (tuc->connection); + return MHD_YES; +} + + +int +AH_handler_truth_post ( + struct MHD_Connection *connection, + struct TM_HandlerContext *hc, + const struct ANASTASIS_CRYPTO_TruthUUIDP *truth_uuid, + const char *truth_data, + size_t *truth_data_size) +{ + struct TruthUploadContext *tuc = hc->ctx; + MHD_RESULT ret; + int res; + struct ANASTASIS_CRYPTO_EncryptedKeyShareP keyshare_data; + void *encrypted_truth; + size_t encrypted_truth_size; + const char *truth_mime; + const char *type; + enum GNUNET_DB_QueryStatus qs; + uint32_t storage_years; + struct GNUNET_TIME_Absolute paid_until; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_fixed_auto ("keyshare_data", + &keyshare_data), + GNUNET_JSON_spec_string ("type", + &type), + GNUNET_JSON_spec_varsize ("encrypted_truth", + &encrypted_truth, + &encrypted_truth_size), + GNUNET_JSON_spec_string ("truth_mime", + &truth_mime), + GNUNET_JSON_spec_uint32 ("storage_duration_years", + &storage_years), + GNUNET_JSON_spec_end () + }; + + if (NULL == tuc) + { + tuc = GNUNET_new (struct TruthUploadContext); + tuc->connection = connection; + tuc->truth_uuid = *truth_uuid; + hc->ctx = tuc; + hc->cc = &cleanup_truth_post; + + /* check for excessive upload */ + { + 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"); + } + } + + { + 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; + + if (1 != sscanf (long_poll_timeout_ms, + "%u", + &timeout)) + { + 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)"); + } + tuc->timeout + = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_relative_multiply ( + GNUNET_TIME_UNIT_MILLISECONDS, + timeout)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Long polling for %u ms enabled\n", + timeout); + } + else + { + tuc->timeout = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_UNIT_SECONDS); + } + } + + } /* end 'if (NULL == tuc)' */ + + if (NULL != tuc->resp) + { + /* We generated a response asynchronously, queue that */ + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Returning asynchronously generated response with HTTP status %u\n", + tuc->response_code); + ret = MHD_queue_response (connection, + tuc->response_code, + tuc->resp); + GNUNET_break (MHD_YES == ret); + MHD_destroy_response (tuc->resp); + tuc->resp = NULL; + return ret; + } + + if (NULL == tuc->json) + { + res = TALER_MHD_parse_post_json (connection, + &tuc->post_ctx, + truth_data, + truth_data_size, + &tuc->json); + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + return MHD_NO; + } + if ( (GNUNET_NO == res) || + (NULL == tuc->json) ) + return MHD_YES; + } + res = TALER_MHD_parse_json_data (connection, + tuc->json, + spec); + if (GNUNET_SYSERR == res) + { + GNUNET_break (0); + return MHD_NO; /* hard failure */ + } + if (GNUNET_NO == res) + { + GNUNET_break_op (0); + return MHD_YES; /* failure */ + } + + /* check method is supported */ + { + struct TALER_Amount dummy; + + if ( (0 != strcmp ("question", + type)) && + (NULL == + ANASTASIS_authorization_plugin_load (type, + AH_cfg, + &dummy)) ) + { + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_ANASTASIS_TRUTH_UPLOAD_METHOD_NOT_SUPPORTED, + type); + } + } + + if (storage_years > ANASTASIS_MAX_YEARS_STORAGE) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "storage_duration_years"); + } + if (0 == storage_years) + storage_years = 1; + + { + struct TALER_Amount zero_amount; + + TALER_amount_set_zero (AH_currency, + &zero_amount); + if (0 != TALER_amount_cmp (&AH_truth_upload_fee, + &zero_amount)) + { + struct GNUNET_TIME_Absolute desired_until; + enum GNUNET_DB_QueryStatus qs; + + desired_until + = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, + storage_years)); + qs = db->check_truth_upload_paid (db->cls, + truth_uuid, + &paid_until); + if (qs < 0) + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_FETCH_FAILED, + NULL); + if ( (0 == qs) || + (paid_until.abs_value_us < desired_until.abs_value_us) ) + { + struct GNUNET_TIME_Absolute now; + struct GNUNET_TIME_Relative rem; + + now = GNUNET_TIME_absolute_get (); + if (paid_until.abs_value_us < now.abs_value_us) + paid_until = now; + rem = GNUNET_TIME_absolute_get_difference (paid_until, + desired_until); + tuc->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)) + tuc->years_to_pay++; + if (0 > + TALER_amount_multiply (&tuc->upload_fee, + &AH_truth_upload_fee, + tuc->years_to_pay)) + { + GNUNET_break_op (0); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_BAD_REQUEST, + TALER_EC_GENERIC_PARAMETER_MALFORMED, + "storage_duration_years"); + } + if ( (0 != tuc->upload_fee.fraction) || + (0 != tuc->upload_fee.value) ) + { + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Truth upload payment required (%d)!\n", + qs); + return begin_payment (tuc); + } + } + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "TRUTH paid until %s (%d)!\n", + GNUNET_STRINGS_relative_time_to_string ( + GNUNET_TIME_absolute_get_remaining ( + paid_until), + GNUNET_YES), + qs); + } + else + { + paid_until + = GNUNET_TIME_relative_to_absolute ( + GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_YEARS, + ANASTASIS_MAX_YEARS_STORAGE)); + } + } + + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Storing truth until %s!\n", + GNUNET_STRINGS_absolute_time_to_string (paid_until)); + qs = db->store_truth (db->cls, + truth_uuid, + &keyshare_data, + truth_mime, + encrypted_truth, + encrypted_truth_size, + type, + GNUNET_TIME_absolute_get_remaining (paid_until)); + switch (qs) + { + case GNUNET_DB_STATUS_HARD_ERROR: + case GNUNET_DB_STATUS_SOFT_ERROR: + GNUNET_break (0); + GNUNET_JSON_parse_free (spec); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_GENERIC_DB_INVARIANT_FAILURE, + "store_truth"); + case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS: + { + void *xtruth; + size_t xtruth_size; + char *xtruth_mime; + char *xmethod; + bool ok = false; + + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == + db->get_escrow_challenge (db->cls, + truth_uuid, + &xtruth, + &xtruth_size, + &xtruth_mime, + &xmethod)) + { + ok = ( (xtruth_size == encrypted_truth_size) && + (0 == strcmp (xmethod, + type)) && + (0 == strcmp (truth_mime, + xtruth_mime)) && + (0 == memcmp (xtruth, + encrypted_truth, + xtruth_size)) ); + GNUNET_free (encrypted_truth); + GNUNET_free (xtruth_mime); + GNUNET_free (xmethod); + } + if (! ok) + { + GNUNET_JSON_parse_free (spec); + + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_CONFLICT, + TALER_EC_ANASTASIS_TRUTH_UPLOAD_UUID_EXISTS, + NULL); + } + /* idempotency detected, intentional fall through! */ + } + case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT: + { + struct MHD_Response *resp; + + GNUNET_JSON_parse_free (spec); + 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); + MHD_destroy_response (resp); + GNUNET_break (MHD_YES == ret); + return ret; + } + } + GNUNET_JSON_parse_free (spec); + GNUNET_break (0); + return MHD_NO; +} |