diff options
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.c | 1001 |
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; -} |