summaryrefslogtreecommitdiff
path: root/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/backend/taler-merchant-httpd_post-tips-ID-pickup.c')
-rw-r--r--src/backend/taler-merchant-httpd_post-tips-ID-pickup.c1001
1 files changed, 0 insertions, 1001 deletions
diff --git a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c b/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
deleted file mode 100644
index 32d78eca..00000000
--- a/src/backend/taler-merchant-httpd_post-tips-ID-pickup.c
+++ /dev/null
@@ -1,1001 +0,0 @@
-/*
- This file is part of TALER
- (C) 2017-2021 Taler Systems SA
-
- TALER 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.
-
- 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 General Public License for more details.
-
- You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
-*/
-/**
- * @file taler-merchant-httpd_post-tips-ID-pickup.c
- * @brief implementation of a POST /tips/ID/pickup handler
- * @author Christian Grothoff
- */
-#include "platform.h"
-#include <microhttpd.h>
-#include <jansson.h>
-#include <taler/taler_json_lib.h>
-#include <taler/taler_signatures.h>
-#include "taler-merchant-httpd.h"
-#include "taler-merchant-httpd_mhd.h"
-#include "taler-merchant-httpd_helper.h"
-#include "taler-merchant-httpd_exchanges.h"
-#include "taler-merchant-httpd_post-tips-ID-pickup.h"
-
-
-/**
- * How often do we retry on serialization errors?
- */
-#define MAX_RETRIES 3
-
-/**
- * How long do we give the exchange operation to complete withdrawing
- * all of the planchets?
- */
-#define EXCHANGE_TIMEOUT GNUNET_TIME_relative_multiply ( \
- GNUNET_TIME_UNIT_SECONDS, 45)
-
-
-/**
- * Active pickup operations.
- */
-struct PickupContext;
-
-
-/**
- * Handle for an individual planchet we are processing for a tip.
- */
-struct PlanchetOperation
-{
- /**
- * Active pickup operation this planchet belongs with.
- */
- struct PickupContext *pc;
-
- /**
- * Kept in a DLL.
- */
- struct PlanchetOperation *prev;
-
- /**
- * Kept in a DLL.
- */
- struct PlanchetOperation *next;
-
- /**
- * Find operation (while active), later NULL.
- */
- struct TMH_EXCHANGES_FindOperation *fo;
-
- /**
- * Withdraw handle (NULL while @e fo is active).
- */
- struct TALER_EXCHANGE_Withdraw2Handle *w2h;
-
- /**
- * Details about the planchet for withdrawing.
- */
- struct TALER_PlanchetDetail pd;
-
- /**
- * Offset of this planchet in the original request.
- */
- unsigned int offset;
-};
-
-
-/**
- * Active pickup operations.
- */
-struct PickupContext
-{
- /**
- * Kept in a DLL.
- */
- struct PickupContext *next;
-
- /**
- * Kept in a DLL.
- */
- struct PickupContext *prev;
-
- /**
- * The connection.
- */
- struct MHD_Connection *connection;
-
- /**
- * Timeout task.
- */
- struct GNUNET_SCHEDULER_Task *tt;
-
- /**
- * Head of DLL of exchange operations on planchets.
- */
- struct PlanchetOperation *po_head;
-
- /**
- * Tail of DLL of exchange operations on planchets.
- */
- struct PlanchetOperation *po_tail;
-
- /**
- * HTTP response to return (set on errors).
- */
- struct MHD_Response *response;
-
- /**
- * Find operation (while active), later NULL.
- */
- struct TMH_EXCHANGES_FindOperation *fo;
-
- /**
- * Which reserve are we draining?
- */
- struct TALER_ReservePrivateKeyP reserve_priv;
-
- /**
- * Which tip is being picked up?
- */
- struct TALER_TipIdentifierP tip_id;
-
- /**
- * What is the ID of the pickup operation? (Basically a
- * hash over the key inputs).
- */
- struct TALER_PickupIdentifierP pickup_id;
-
- /**
- * Array of our planchets.
- */
- struct TALER_PlanchetDetail *planchets;
-
- /**
- * Length of the @e planchets array.
- */
- unsigned int planchets_length;
-
- /**
- * HTTP status to use (set on errors).
- */
- unsigned int http_status;
-
- /**
- * Total amount requested in the pick up operation. Computed by
- * totaling up the amounts of all the @e planchets.
- */
- struct TALER_Amount total_requested;
-
- /**
- * True if @e total_requested has been initialized.
- */
- bool tr_initialized;
-};
-
-
-/**
- * Head of DLL.
- */
-static struct PickupContext *pc_head;
-
-/**
- * Tail of DLL.
- */
-static struct PickupContext *pc_tail;
-
-
-/**
- * Stop all ongoing operations associated with @a pc.
- */
-static void
-stop_operations (struct PickupContext *pc)
-{
- struct PlanchetOperation *po;
-
- if (NULL != pc->tt)
- {
- GNUNET_SCHEDULER_cancel (pc->tt);
- pc->tt = NULL;
- }
- if (NULL != pc->fo)
- {
- TMH_EXCHANGES_find_exchange_cancel (pc->fo);
- pc->fo = NULL;
- }
- while (NULL != (po = pc->po_head))
- {
- if (NULL != po->fo)
- {
- TMH_EXCHANGES_find_exchange_cancel (po->fo);
- po->fo = NULL;
- }
- if (NULL != po->w2h)
- {
- TALER_EXCHANGE_withdraw2_cancel (po->w2h);
- po->w2h = NULL;
- }
- GNUNET_CONTAINER_DLL_remove (pc->po_head,
- pc->po_tail,
- po);
- GNUNET_free (po);
- }
-}
-
-
-/**
- * Function called to clean up.
- *
- * @param cls a `struct PickupContext *` to clean up
- */
-static void
-pick_context_cleanup (void *cls)
-{
- struct PickupContext *pc = cls;
-
- stop_operations (pc); /* should not be any... */
- for (unsigned int i = 0; i<pc->planchets_length; i++)
- TALER_planchet_detail_free (&pc->planchets[i]);
- GNUNET_array_grow (pc->planchets,
- pc->planchets_length,
- 0);
- GNUNET_free (pc);
-}
-
-
-void
-TMH_force_tip_pickup_resume ()
-{
- struct PickupContext *nxt;
-
- for (struct PickupContext *pc = pc_head;
- NULL != pc;
- pc = nxt)
- {
- nxt = pc->next;
- stop_operations (pc);
- GNUNET_CONTAINER_DLL_remove (pc_head,
- pc_tail,
- pc);
- MHD_resume_connection (pc->connection);
- }
-}
-
-
-/**
- * Callbacks of this type are used to serve the result of submitting a
- * withdraw request to a exchange without the (un)blinding factor.
- * We persist the result in the database and, if we were the last
- * planchet operation, resume HTTP processing.
- *
- * @param cls closure with a `struct PlanchetOperation *`
- * @param hr HTTP response data
- * @param blind_sig blind signature over the coin, NULL on error
- */
-static void
-withdraw_cb (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- const struct TALER_BlindedDenominationSignature *blind_sig)
-{
- struct PlanchetOperation *po = cls;
- struct PickupContext *pc = po->pc;
- enum GNUNET_DB_QueryStatus qs;
-
- GNUNET_CONTAINER_DLL_remove (pc->po_head,
- pc->po_tail,
- po);
- TMH_db->preflight (TMH_db->cls);
- if (NULL == blind_sig)
- {
- GNUNET_free (po);
- stop_operations (pc);
- pc->http_status = MHD_HTTP_BAD_GATEWAY;
- pc->response =
- TALER_MHD_MAKE_JSON_PACK (
- TALER_JSON_pack_ec (TALER_EC_MERCHANT_TIP_PICKUP_EXCHANGE_ERROR),
- TMH_pack_exchange_reply (hr));
- GNUNET_CONTAINER_DLL_remove (pc_head,
- pc_tail,
- pc);
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- qs = TMH_db->insert_pickup_blind_signature (TMH_db->cls,
- &pc->pickup_id,
- po->offset,
- blind_sig);
- GNUNET_free (po);
- if (qs < 0)
- {
- stop_operations (pc);
- pc->http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
- pc->response = TALER_MHD_make_error (
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "blind signature");
- GNUNET_CONTAINER_DLL_remove (pc_head,
- pc_tail,
- pc);
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- if (NULL == pc->po_head)
- {
- stop_operations (pc); /* stops timeout job */
- GNUNET_CONTAINER_DLL_remove (pc_head,
- pc_tail,
- pc);
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- }
-}
-
-
-/**
- * Function called with the result of a #TMH_EXCHANGES_find_exchange()
- * operation as part of a withdraw objective. If the exchange is ready,
- * withdraws the planchet from the exchange.
- *
- * @param cls closure, with our `struct PlanchetOperation *`
- * @param hr HTTP response details
- * @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
- * @param exchange_trusted true if this exchange is trusted by config
- */
-static void
-do_withdraw (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
- bool exchange_trusted)
-{
- struct PlanchetOperation *po = cls;
- struct PickupContext *pc = po->pc;
-
- po->fo = NULL;
- TMH_db->preflight (TMH_db->cls);
- if (NULL == hr)
- {
- stop_operations (pc);
- GNUNET_CONTAINER_DLL_remove (pc->po_head,
- pc->po_tail,
- po);
- GNUNET_free (po);
- pc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
- pc->response = TALER_MHD_MAKE_JSON_PACK (
- TALER_JSON_pack_ec (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT));
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- if (NULL == eh)
- {
- stop_operations (pc);
- GNUNET_CONTAINER_DLL_remove (pc->po_head,
- pc->po_tail,
- po);
- GNUNET_free (po);
- pc->http_status = MHD_HTTP_BAD_GATEWAY;
- pc->response =
- TALER_MHD_MAKE_JSON_PACK (
- TALER_JSON_pack_ec (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
- TMH_pack_exchange_reply (hr));
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- po->w2h = TALER_EXCHANGE_withdraw2 (eh,
- &po->pd,
- &pc->reserve_priv,
- &withdraw_cb,
- po);
-}
-
-
-/**
- * Withdraw @a planchet from @a exchange_url for @a pc operation at planchet
- * @a offset. Sets up the respective operation and adds it @a pc's operation
- * list. Once the operation is complete, the resulting blind signature is
- * committed to the merchant's database. If all planchet operations are
- * completed, the HTTP processing is resumed.
- *
- * @param[in,out] pc a pending pickup operation that includes @a planchet
- * @param exchange_url identifies an exchange to do the pickup from
- * @param planchet details about the coin to pick up
- * @param offset offset of @a planchet in the list, needed to process the reply
- */
-static void
-try_withdraw (struct PickupContext *pc,
- const char *exchange_url,
- const struct TALER_PlanchetDetail *planchet,
- unsigned int offset)
-{
- struct PlanchetOperation *po;
-
- TMH_db->preflight (TMH_db->cls);
- po = GNUNET_new (struct PlanchetOperation);
- po->pc = pc;
- po->pd = *planchet;
- po->offset = offset;
- po->fo = TMH_EXCHANGES_find_exchange (exchange_url,
- NULL,
- GNUNET_NO,
- &do_withdraw,
- po);
- GNUNET_assert (NULL != po->fo);
- GNUNET_CONTAINER_DLL_insert (pc->po_head,
- pc->po_tail,
- po);
-}
-
-
-/**
- * Handle timeout for pickup.
- *
- * @param cls a `struct PickupContext *`
- */
-static void
-do_timeout (void *cls)
-{
- struct PickupContext *pc = cls;
-
- pc->tt = NULL;
- stop_operations (pc);
- pc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
- pc->response = TALER_MHD_make_error (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT,
- NULL);
- GNUNET_CONTAINER_DLL_remove (pc_head,
- pc_tail,
- pc);
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
-}
-
-
-/**
- * Function called with the result of a #TMH_EXCHANGES_find_exchange()
- * operation as part of a withdraw objective. Here, we initialize
- * the "total_requested" amount by adding up the cost of the planchets
- * provided by the client.
- *
- * @param cls closure, with our `struct PickupContext *`
- * @param hr HTTP response details
- * @param eh handle to the exchange context
- * @param payto_uri payto://-URI of the exchange
- * @param wire_fee current applicable wire fee for dealing with @a eh, NULL if not available
- * @param exchange_trusted true if this exchange is trusted by config
- */
-static void
-compute_total_requested (void *cls,
- const struct TALER_EXCHANGE_HttpResponse *hr,
- struct TALER_EXCHANGE_Handle *eh,
- const char *payto_uri,
- const struct TALER_Amount *wire_fee,
- bool exchange_trusted)
-{
- struct PickupContext *pc = cls;
- const struct TALER_EXCHANGE_Keys *keys;
-
- pc->fo = NULL;
- stop_operations (pc); /* stops timeout job */
- if (NULL == hr)
- {
- pc->http_status = MHD_HTTP_GATEWAY_TIMEOUT;
- pc->response = TALER_MHD_MAKE_JSON_PACK (
- TALER_JSON_pack_ec (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_TIMEOUT));
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- if (NULL == eh)
- {
- pc->http_status = MHD_HTTP_BAD_GATEWAY;
- pc->response =
- TALER_MHD_MAKE_JSON_PACK (
- TALER_JSON_pack_ec (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_CONNECT_FAILURE),
- TMH_pack_exchange_reply (hr));
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- if (NULL == (keys = TALER_EXCHANGE_get_keys (eh)))
- {
- pc->http_status = MHD_HTTP_BAD_GATEWAY;
- pc->response =
- TALER_MHD_MAKE_JSON_PACK (
- TALER_JSON_pack_ec (
- TALER_EC_MERCHANT_GENERIC_EXCHANGE_KEYS_FAILURE),
- TMH_pack_exchange_reply (hr));
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- GNUNET_assert (GNUNET_OK ==
- TALER_amount_set_zero (TMH_currency,
- &pc->total_requested));
- for (unsigned int i = 0; i<pc->planchets_length; i++)
- {
- struct TALER_PlanchetDetail *pd = &pc->planchets[i];
- const struct TALER_EXCHANGE_DenomPublicKey *dpk;
-
- dpk = TALER_EXCHANGE_get_denomination_key_by_hash (keys,
- &pd->denom_pub_hash);
- if (NULL == dpk)
- {
- pc->http_status = MHD_HTTP_CONFLICT;
- pc->response =
- TALER_MHD_MAKE_JSON_PACK (
- TALER_JSON_pack_ec (
- TALER_EC_MERCHANT_TIP_PICKUP_DENOMINATION_UNKNOWN),
- TMH_pack_exchange_reply (hr));
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
-
- if ( (GNUNET_YES !=
- TALER_amount_cmp_currency (&pc->total_requested,
- &dpk->value)) ||
- (0 >
- TALER_amount_add (&pc->total_requested,
- &pc->total_requested,
- &dpk->value)) )
- {
- pc->http_status = MHD_HTTP_BAD_REQUEST;
- pc->response =
- TALER_MHD_make_error (TALER_EC_MERCHANT_TIP_PICKUP_SUMMATION_FAILED,
- "Could not add up values to compute pickup total");
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
- return;
- }
- }
- pc->tr_initialized = true;
- MHD_resume_connection (pc->connection);
- TALER_MHD_daemon_trigger (); /* we resumed, kick MHD */
-}
-
-
-/**
- * The tip lookup operation failed. Generate an error response based on the @a qs.
- *
- * @param connection connection to generate error for
- * @param qs DB status to base error creation on
- * @return MHD result code
- */
-static MHD_RESULT
-reply_lookup_tip_failed (struct MHD_Connection *connection,
- enum GNUNET_DB_QueryStatus qs)
-{
- unsigned int response_code;
- enum TALER_ErrorCode ec;
-
- TMH_db->rollback (TMH_db->cls);
- switch (qs)
- {
- case GNUNET_DB_STATUS_SUCCESS_NO_RESULTS:
- ec = TALER_EC_MERCHANT_GENERIC_TIP_ID_UNKNOWN;
- response_code = MHD_HTTP_NOT_FOUND;
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- ec = TALER_EC_GENERIC_DB_SOFT_FAILURE;
- response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- break;
- case GNUNET_DB_STATUS_HARD_ERROR:
- ec = TALER_EC_GENERIC_DB_COMMIT_FAILED;
- response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- break;
- default:
- GNUNET_break (0);
- ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- break;
- }
- return TALER_MHD_reply_with_error (connection,
- response_code,
- ec,
- NULL);
-}
-
-
-MHD_RESULT
-TMH_post_tips_ID_pickup (const struct TMH_RequestHandler *rh,
- struct MHD_Connection *connection,
- struct TMH_HandlerContext *hc)
-{
- struct PickupContext *pc = hc->ctx;
- char *exchange_url;
- struct TALER_Amount total_authorized;
- struct TALER_Amount total_picked_up;
- struct TALER_Amount total_remaining;
- struct GNUNET_TIME_Timestamp expiration;
- enum GNUNET_DB_QueryStatus qs;
- unsigned int num_retries;
-
- if (NULL == pc)
- {
- json_t *planchets;
- json_t *planchet;
- size_t index;
-
- pc = GNUNET_new (struct PickupContext);
- hc->ctx = pc;
- hc->cc = &pick_context_cleanup;
-
- GNUNET_assert (NULL != hc->infix);
- if (GNUNET_OK !=
- GNUNET_CRYPTO_hash_from_string (hc->infix,
- &pc->tip_id.hash))
- {
- /* tip_id has wrong encoding */
- GNUNET_break_op (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- hc->infix);
- }
-
- {
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_json ("planchets",
- &planchets),
- GNUNET_JSON_spec_end ()
- };
- {
- enum GNUNET_GenericReturnValue res;
-
- res = TALER_MHD_parse_json_data (connection,
- hc->request_body,
- spec);
- if (GNUNET_OK != res)
- return (GNUNET_NO == res)
- ? MHD_YES
- : MHD_NO;
- }
- }
- if (! json_is_array (planchets))
- {
- GNUNET_break_op (0);
- json_decref (planchets);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_GENERIC_PARAMETER_MALFORMED,
- "planchets");
- }
-
- GNUNET_array_grow (pc->planchets,
- pc->planchets_length,
- json_array_size (planchets));
- json_array_foreach (planchets, index, planchet) {
- struct TALER_PlanchetDetail *pd = &pc->planchets[index];
- struct GNUNET_JSON_Specification spec[] = {
- GNUNET_JSON_spec_fixed_auto ("denom_pub_hash",
- &pd->denom_pub_hash),
- TALER_JSON_spec_blinded_planchet ("coin_ev",
- &pd->blinded_planchet),
- GNUNET_JSON_spec_end ()
- };
- {
- enum GNUNET_GenericReturnValue res;
-
- res = TALER_MHD_parse_json_data (connection,
- planchet,
- spec);
- if (GNUNET_OK != res)
- {
- json_decref (planchets);
- return (GNUNET_NO == res)
- ? MHD_YES
- : MHD_NO;
- }
- }
- }
- json_decref (planchets);
- {
- struct GNUNET_HashContext *hc;
-
- hc = GNUNET_CRYPTO_hash_context_start ();
- GNUNET_CRYPTO_hash_context_read (hc,
- &pc->tip_id,
- sizeof (pc->tip_id));
- for (unsigned int i = 0; i<pc->planchets_length; i++)
- {
- struct TALER_PlanchetDetail *pd = &pc->planchets[i];
-
- GNUNET_CRYPTO_hash_context_read (hc,
- &pd->denom_pub_hash,
- sizeof (pd->denom_pub_hash));
- TALER_blinded_planchet_hash_ (&pd->blinded_planchet,
- hc);
- }
- GNUNET_CRYPTO_hash_context_finish (hc,
- &pc->pickup_id.hash);
- }
- }
-
- if (NULL != pc->response)
- {
- MHD_RESULT ret;
-
- ret = MHD_queue_response (connection,
- pc->http_status,
- pc->response);
- pc->response = NULL;
- return ret;
- }
-
- if (! pc->tr_initialized)
- {
- qs = TMH_db->lookup_tip (TMH_db->cls,
- hc->instance->settings.id,
- &pc->tip_id,
- &total_authorized,
- &total_picked_up,
- &expiration,
- &exchange_url,
- &pc->reserve_priv);
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- return reply_lookup_tip_failed (connection,
- qs);
- MHD_suspend_connection (connection);
- pc->connection = connection;
- pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT,
- &do_timeout,
- pc);
- pc->fo = TMH_EXCHANGES_find_exchange (exchange_url,
- NULL,
- GNUNET_NO,
- &compute_total_requested,
- pc);
- GNUNET_free (exchange_url);
- return MHD_YES;
- }
-
-
- TMH_db->preflight (TMH_db->cls);
- num_retries = 0;
-RETRY:
- num_retries++;
- if (num_retries > MAX_RETRIES)
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_SOFT_FAILURE,
- NULL);
- }
- if (GNUNET_OK !=
- TMH_db->start (TMH_db->cls,
- "pickup tip"))
- {
- GNUNET_break (0);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_START_FAILED,
- NULL);
- }
- {
- struct TALER_BlindedDenominationSignature sigs[
- GNUNET_NZL (pc->planchets_length)];
-
- memset (sigs,
- 0,
- sizeof (sigs));
- qs = TMH_db->lookup_pickup (TMH_db->cls,
- hc->instance->settings.id,
- &pc->tip_id,
- &pc->pickup_id,
- &exchange_url,
- &pc->reserve_priv,
- pc->planchets_length,
- sigs);
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Lookup pickup `%s' resulted in %d\n",
- GNUNET_h2s (&pc->pickup_id.hash),
- qs);
- if (qs > GNUNET_DB_STATUS_SUCCESS_ONE_RESULT)
- qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT;
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs)
- {
- bool rollback = false;
-
- for (unsigned int i = 0; i< pc->planchets_length; i++)
- {
- if (TALER_DENOMINATION_INVALID != sigs[i].cipher)
- continue;
- if (! rollback)
- {
- TMH_db->rollback (TMH_db->cls);
- MHD_suspend_connection (connection);
- GNUNET_CONTAINER_DLL_insert (pc_head,
- pc_tail,
- pc);
- pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT,
- &do_timeout,
- pc);
- rollback = true;
- }
- GNUNET_log (GNUNET_ERROR_TYPE_INFO,
- "Lookup pickup `%s' initiated withdraw #%u\n",
- GNUNET_h2s (&pc->pickup_id.hash),
- i);
- try_withdraw (pc,
- exchange_url,
- &pc->planchets[i],
- i);
- }
- GNUNET_free (exchange_url);
- if (rollback)
- return MHD_YES;
- /* we got _all_ signatures, can continue! */
- }
- if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != qs)
- {
- unsigned int response_code;
- enum TALER_ErrorCode ec;
-
- TMH_db->rollback (TMH_db->cls);
- switch (qs)
- {
- case GNUNET_DB_STATUS_SUCCESS_ONE_RESULT:
- {
- json_t *blind_sigs;
-
- blind_sigs = json_array ();
- GNUNET_assert (NULL != blind_sigs);
- for (unsigned int i = 0; i<pc->planchets_length; i++)
- {
- GNUNET_assert (0 ==
- json_array_append_new (
- blind_sigs,
- GNUNET_JSON_PACK (
- TALER_JSON_pack_blinded_denom_sig ("blind_sig",
- &sigs[i]))));
- TALER_blinded_denom_sig_free (&sigs[i]);
- }
- return TALER_MHD_REPLY_JSON_PACK (
- connection,
- MHD_HTTP_OK,
- GNUNET_JSON_pack_array_steal ("blind_sigs",
- blind_sigs));
- }
- break;
- case GNUNET_DB_STATUS_SOFT_ERROR:
- goto RETRY;
- case GNUNET_DB_STATUS_HARD_ERROR:
- ec = TALER_EC_GENERIC_DB_FETCH_FAILED;
- response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- break;
- default:
- GNUNET_break (0);
- ec = TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE;
- response_code = MHD_HTTP_INTERNAL_SERVER_ERROR;
- break;
- }
- return TALER_MHD_reply_with_error (connection,
- response_code,
- ec,
- NULL);
- }
- }
-
- qs = TMH_db->lookup_tip (TMH_db->cls,
- hc->instance->settings.id,
- &pc->tip_id,
- &total_authorized,
- &total_picked_up,
- &expiration,
- &exchange_url,
- &pc->reserve_priv);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- {
- TMH_db->rollback (TMH_db->cls);
- goto RETRY;
- }
- if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs)
- {
- TMH_db->rollback (TMH_db->cls);
- return reply_lookup_tip_failed (connection,
- qs);
- }
- if (GNUNET_TIME_absolute_is_past (expiration.abs_time))
- {
- GNUNET_free (exchange_url);
- TMH_db->rollback (TMH_db->cls);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_GONE,
- TALER_EC_MERCHANT_TIP_PICKUP_HAS_EXPIRED,
- hc->infix);
- }
- if (0 >
- TALER_amount_subtract (&total_remaining,
- &total_authorized,
- &total_picked_up))
- {
- GNUNET_free (exchange_url);
- GNUNET_break_op (0);
- TMH_db->rollback (TMH_db->cls);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE,
- "picked up amount exceeds authorized amount");
- }
-
- if (0 >
- TALER_amount_cmp (&total_remaining,
- &pc->total_requested))
- {
- /* total_remaining < pc->total_requested */
- GNUNET_free (exchange_url);
- GNUNET_break_op (0);
- TMH_db->rollback (TMH_db->cls);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_BAD_REQUEST,
- TALER_EC_MERCHANT_TIP_PICKUP_AMOUNT_EXCEEDS_TIP_REMAINING,
- hc->infix);
- }
-
- GNUNET_assert (0 <
- TALER_amount_add (&total_picked_up,
- &total_picked_up,
- &pc->total_requested));
- qs = TMH_db->insert_pickup (TMH_db->cls,
- hc->instance->settings.id,
- &pc->tip_id,
- &total_picked_up,
- &pc->pickup_id,
- &pc->total_requested);
- if (qs < 0)
- {
- TMH_db->rollback (TMH_db->cls);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- GNUNET_free (exchange_url);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_STORE_FAILED,
- "pickup");
- }
- qs = TMH_db->commit (TMH_db->cls);
- if (qs < 0)
- {
- TMH_db->rollback (TMH_db->cls);
- if (GNUNET_DB_STATUS_SOFT_ERROR == qs)
- goto RETRY;
- GNUNET_free (exchange_url);
- return TALER_MHD_reply_with_error (connection,
- MHD_HTTP_INTERNAL_SERVER_ERROR,
- TALER_EC_GENERIC_DB_COMMIT_FAILED,
- NULL);
- }
- MHD_suspend_connection (connection);
- GNUNET_CONTAINER_DLL_insert (pc_head,
- pc_tail,
- pc);
- pc->tt = GNUNET_SCHEDULER_add_delayed (EXCHANGE_TIMEOUT,
- &do_timeout,
- pc);
- for (unsigned int i = 0; i<pc->planchets_length; i++)
- {
- try_withdraw (pc,
- exchange_url,
- &pc->planchets[i],
- i);
- }
- GNUNET_free (exchange_url);
- return MHD_YES;
-}