/*
This file is part of TALER
(C) 2020 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 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
*/
/**
* @file taler-merchant-httpd_private-post-reserves.c
* @brief implementing POST /reserves request handling
* @author Christian Grothoff
*/
#include "platform.h"
#include "taler-merchant-httpd_exchanges.h"
#include "taler-merchant-httpd_private-post-reserves.h"
#include "taler-merchant-httpd_reserves.h"
#include
/**
* Information we keep for an individual call to the POST /reserves handler.
*/
struct PostReserveContext
{
/**
* Stored in a DLL.
*/
struct PostReserveContext *next;
/**
* Stored in a DLL.
*/
struct PostReserveContext *prev;
/**
* Array with @e coins_cnt coins we are despositing.
*/
struct DepositConfirmation *dc;
/**
* MHD connection to return to
*/
struct MHD_Connection *connection;
/**
* Details about the client's request.
*/
struct TMH_HandlerContext *hc;
/**
* URL of the exchange.
*/
const char *exchange_url;
/**
* URI of the exchange where the payment needs to be made to.
*/
char *payto_uri;
/**
* Handle for contacting the exchange.
*/
struct TMH_EXCHANGES_FindOperation *fo;
/**
* Initial balance of the reserve.
*/
struct TALER_Amount initial_balance;
/**
* When will the reserve expire.
*/
struct GNUNET_TIME_Absolute reserve_expiration;
/**
* Which HTTP status should we return?
*/
unsigned int http_status;
/**
* Which error code should we return?
*/
enum TALER_ErrorCode ec;
/**
* Are we suspended?
*/
bool suspended;
};
/**
* Stored in a DLL.
*/
static struct PostReserveContext *rc_head;
/**
* Stored in a DLL.
*/
static struct PostReserveContext *rc_tail;
/**
* Force all post reserve contexts to be resumed as we are about
* to shut down MHD.
*/
void
TMH_force_rc_resume ()
{
for (struct PostReserveContext *rc = rc_head;
NULL != rc;
rc = rc->next)
{
if (rc->suspended)
{
rc->suspended = false;
MHD_resume_connection (rc->connection);
GNUNET_CONTAINER_DLL_remove (rc_head,
rc_tail,
rc);
}
if (NULL != rc->fo)
{
TMH_EXCHANGES_find_exchange_cancel (rc->fo);
rc->fo = NULL;
}
}
}
/**
* Custom cleanup routine for a `struct PostReserveContext`.
*
* @param cls the `struct PostReserveContext` to clean up.
*/
static void
reserve_context_cleanup (void *cls)
{
struct PostReserveContext *rc = cls;
if (NULL != rc->fo)
{
TMH_EXCHANGES_find_exchange_cancel (rc->fo);
rc->fo = NULL;
}
GNUNET_assert (! rc->suspended);
GNUNET_free (rc->payto_uri);
GNUNET_free (rc);
}
/**
* Function called with the result of a #TMH_EXCHANGES_find_exchange()
* operation.
*
* @param cls closure with our `struct PostReserveContext *`
* @param hr HTTP response details
* @param payto_uri URI of the exchange for the wire transfer, NULL on errors
* @param eh handle to the exchange context
* @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
handle_exchange (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 PostReserveContext *rc = cls;
const struct TALER_EXCHANGE_Keys *keys;
rc->fo = NULL;
rc->suspended = false;
MHD_resume_connection (rc->connection);
GNUNET_CONTAINER_DLL_remove (rc_head,
rc_tail,
rc);
if (NULL == hr)
{
rc->ec = TALER_EC_TIMEOUT;
rc->http_status = MHD_HTTP_REQUEST_TIMEOUT;
TMH_trigger_daemon (); /* we resumed, kick MHD */
return;
}
keys = TALER_EXCHANGE_get_keys (eh);
if (NULL == keys)
{
rc->ec = TALER_EC_KEYS_INVALID;
rc->http_status = MHD_HTTP_FAILED_DEPENDENCY;
TMH_trigger_daemon (); /* we resumed, kick MHD */
return;
}
if (NULL == payto_uri)
{
rc->ec = TALER_EC_RESERVES_POST_UNSUPPORTED_WIRE_METHOD;
rc->http_status = MHD_HTTP_CONFLICT;
TMH_trigger_daemon (); /* we resumed, kick MHD */
return;
}
rc->reserve_expiration
= GNUNET_TIME_relative_to_absolute (keys->reserve_closing_delay);
rc->payto_uri = GNUNET_strdup (payto_uri);
TMH_trigger_daemon (); /* we resumed, kick MHD */
}
MHD_RESULT
TMH_private_post_reserves (const struct TMH_RequestHandler *rh,
struct MHD_Connection *connection,
struct TMH_HandlerContext *hc)
{
struct PostReserveContext *rc = hc->ctx;
struct TMH_MerchantInstance *mi = hc->instance;
GNUNET_assert (NULL != mi);
if (NULL == rc)
{
const char *wire_method;
rc = GNUNET_new (struct PostReserveContext);
rc->connection = connection;
rc->hc = hc;
hc->ctx = rc;
hc->cc = &reserve_context_cleanup;
{
enum GNUNET_GenericReturnValue res;
struct GNUNET_JSON_Specification spec[] = {
GNUNET_JSON_spec_string ("exchange_url",
&rc->exchange_url),
GNUNET_JSON_spec_string ("wire_method",
&wire_method),
TALER_JSON_spec_amount ("initial_balance",
&rc->initial_balance),
GNUNET_JSON_spec_end ()
};
res = TALER_MHD_parse_json_data (connection,
hc->request_body,
spec);
if (GNUNET_OK != res)
return (GNUNET_NO == res)
? MHD_YES
: MHD_NO;
}
rc->fo = TMH_EXCHANGES_find_exchange (rc->exchange_url,
wire_method,
GNUNET_NO,
&handle_exchange,
rc);
rc->suspended = true;
GNUNET_CONTAINER_DLL_insert (rc_head,
rc_tail,
rc);
MHD_suspend_connection (connection);
return MHD_YES;
}
GNUNET_assert (! rc->suspended);
if (NULL == rc->payto_uri)
{
return TALER_MHD_reply_with_error (connection,
rc->http_status,
rc->ec,
NULL);
}
{
struct TALER_ReservePublicKeyP reserve_pub;
struct TALER_ReservePrivateKeyP reserve_priv;
enum GNUNET_DB_QueryStatus qs;
GNUNET_CRYPTO_eddsa_key_create (&reserve_priv.eddsa_priv);
GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv.eddsa_priv,
&reserve_pub.eddsa_pub);
qs = TMH_db->insert_reserve (TMH_db->cls,
mi->settings.id,
&reserve_priv,
&reserve_pub,
rc->exchange_url,
&rc->initial_balance,
rc->reserve_expiration);
GNUNET_break (GNUNET_DB_STATUS_SOFT_ERROR != qs);
TMH_RESERVES_check (mi->settings.id,
rc->exchange_url,
&reserve_pub,
&rc->initial_balance);
if (qs < 0)
return TALER_MHD_reply_with_error (connection,
MHD_HTTP_INTERNAL_SERVER_ERROR,
TALER_EC_RESERVES_POST_DB_COMMIT_HARD_ERROR,
NULL);
return TALER_MHD_reply_json_pack (connection,
MHD_HTTP_OK,
"{s:o,s:s}",
"reserve_pub",
GNUNET_JSON_from_data_auto (&reserve_pub),
"payto_uri",
rc->payto_uri);
}
}
/* end of taler-merchant-httpd_private-post-reserves.c */