summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2015-11-25 16:40:45 +0100
committerChristian Grothoff <christian@grothoff.org>2015-11-25 16:40:45 +0100
commit698519e581cc5822ba68eb687e19ab8f10f5a093 (patch)
tree832bd06d049a87a36262b86af7dccb28e7ee6a77
parent85cc2554fe5537f79b3c5bb1700faeadb8bad99e (diff)
downloadmerchant-698519e581cc5822ba68eb687e19ab8f10f5a093.tar.gz
merchant-698519e581cc5822ba68eb687e19ab8f10f5a093.tar.bz2
merchant-698519e581cc5822ba68eb687e19ab8f10f5a093.zip
major refactoring of taler-merchant-httpd_pay, compiles but cannot yet work (uninitialized fields, some logic still broken, etc.)
-rw-r--r--src/backend/Makefile.am1
-rw-r--r--src/backend/taler-merchant-httpd.c236
-rw-r--r--src/backend/taler-merchant-httpd.h112
-rw-r--r--src/backend/taler-merchant-httpd_contract.c19
-rw-r--r--src/backend/taler-merchant-httpd_pay.c681
-rw-r--r--src/backend/taler-merchant-httpd_responses.c107
-rw-r--r--src/backend/taler-merchant-httpd_responses.h48
7 files changed, 501 insertions, 703 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am
index b1936990..8d730f3e 100644
--- a/src/backend/Makefile.am
+++ b/src/backend/Makefile.am
@@ -9,6 +9,7 @@ taler_merchant_httpd_SOURCES = \
taler-merchant-httpd_parsing.c taler-merchant-httpd_parsing.h \
taler-merchant-httpd_responses.c taler-merchant-httpd_responses.h \
taler-merchant-httpd_mhd.c taler-merchant-httpd_mhd.h \
+ taler-merchant-httpd_mints.c taler-merchant-httpd_mints.h \
taler-merchant-httpd_contract.c taler-merchant-httpd_contract.h \
taler-merchant-httpd_pay.c taler-merchant-httpd_pay.h
diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c
index 8a32b2ba..83c902fd 100644
--- a/src/backend/taler-merchant-httpd.c
+++ b/src/backend/taler-merchant-httpd.c
@@ -19,8 +19,8 @@
* @brief HTTP serving layer intended to perform crypto-work and
* communication with the mint
* @author Marcello Stanisci
+ * @author Christian Grothoff
*/
-
#include "platform.h"
#include <microhttpd.h>
#include <jansson.h>
@@ -34,6 +34,7 @@
#include "taler-merchant-httpd.h"
#include "taler-merchant-httpd_mhd.h"
#include "taler-merchant-httpd_contract.h"
+#include "taler-merchant-httpd_mints.h"
#include "taler-merchant-httpd_pay.h"
@@ -48,13 +49,17 @@ struct json_t *j_wire;
*/
struct GNUNET_HashCode h_wire;
-
/**
* Merchant's private key
*/
struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
/**
+ * Merchant's public key
+ */
+struct TALER_MerchantPublicKeyP pubkey;
+
+/**
* Our hostname
*/
static char *hostname;
@@ -81,11 +86,6 @@ struct GNUNET_TIME_Relative edate_delay;
char *TMH_merchant_currency_string;
/**
- * Trusted mints (FIXME: they are NOT all trusted!).
- */
-struct MERCHANT_Mint **mints;
-
-/**
* Active auditors
*/
struct MERCHANT_Auditor *auditors;
@@ -101,11 +101,6 @@ static struct GNUNET_SCHEDULER_Task *shutdown_task;
static struct GNUNET_SCHEDULER_Task *mhd_task;
/**
- * Length of the #mints array.
- */
-unsigned int nmints;
-
-/**
* The number of active auditors
*/
unsigned int nauditors;
@@ -187,15 +182,9 @@ url_handler (void *cls,
"Hello, I'm a merchant's Taler backend. This HTTP server is not for humans.\n", 0,
&TMH_MHD_handler_static_response, MHD_HTTP_OK },
- /* Further test page */
- { "/hello", MHD_HTTP_METHOD_GET, "text/plain",
- "Hello, Customer.\n", 0,
- &TMH_MHD_handler_static_response, MHD_HTTP_OK },
-
{ "/contract", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_contract, MHD_HTTP_OK },
-
{ "/contract", NULL, "text/plain",
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
@@ -203,33 +192,24 @@ url_handler (void *cls,
{ "/pay", MHD_HTTP_METHOD_POST, "application/json",
NULL, 0,
&MH_handler_pay, MHD_HTTP_OK },
-
{ "/pay", NULL, "text/plain",
"Only POST is allowed", 0,
&TMH_MHD_handler_send_json_pack_error, MHD_HTTP_METHOD_NOT_ALLOWED },
-
{NULL, NULL, NULL, NULL, 0, 0 }
};
-
static struct TMH_RequestHandler h404 =
{
"", NULL, "text/html",
"<html><title>404: not found</title></html>", 0,
&TMH_MHD_handler_static_response, MHD_HTTP_NOT_FOUND
};
-
- /* Compiler complains about non returning a value in a non-void
- declared function: the FIX is to return what the handler for
- a particular URL returns */
-
struct TMH_RequestHandler *rh;
unsigned int i;
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
- "Handling request for URL '%s'\n",
+ "Handling request for URL `%s'\n",
url);
-
for (i=0;NULL != handlers[i].url;i++)
{
rh = &handlers[i];
@@ -249,43 +229,6 @@ url_handler (void *cls,
con_cls,
upload_data,
upload_data_size);
-
-}
-
-
-/**
- * Function called with information about who is auditing
- * a particular mint and what key the mint is using.
- *
- * @param cls closure, will be 'struct MERCHANT_Mint' so that
- * when this function gets called, it will change the flag 'pending'
- * to 'false'. Note: 'keys' is automatically saved inside the mint's
- * handle, which is contained inside 'struct MERCHANT_Mint', when
- * this callback is called. Thus, once 'pending' turns 'false',
- * it is safe to call 'TALER_MINT_get_keys()' on the mint's handle,
- * in order to get the "good" keys.
- *
- * @param keys information about the various keys used
- * by the mint
- */
-static void
-keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
-{
- /* HOT UPDATE: the merchants need the denomination keys!
- Because it wants to (firstly) verify the deposit confirmation
- sent by the mint, and the signed blob depends (among the
- other things) on the coin's deposit fee. That information
- is never communicated by the wallet to the merchant.
- Again, the merchant needs it because it wants to verify that
- the wallet didn't exceede the limit imposed by the merchant
- on the total deposit fee for a purchase */
-
- if (NULL != keys)
- {
- ((struct MERCHANT_Mint *) cls)->pending = 0;
- }
- else
- printf ("no keys gotten\n");
}
@@ -299,18 +242,6 @@ keys_mgmt_cb (void *cls, const struct TALER_MINT_Keys *keys)
static void
do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
{
- unsigned int cnt;
-
- for (cnt = 0; cnt < nmints; cnt++)
- {
- if (NULL != mints[cnt]->conn)
- TALER_MINT_disconnect (mints[cnt]->conn);
- if (NULL != mints[cnt]->poller_task)
- {
- GNUNET_SCHEDULER_cancel (mints[cnt]->poller_task);
- mints[cnt]->poller_task = NULL;
- }
- }
if (NULL != mhd_task)
{
GNUNET_SCHEDULER_cancel (mhd_task);
@@ -332,65 +263,6 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
/**
- * Task that runs the context's event loop using the GNUnet scheduler.
- *
- * @param cls a `struct MERCHANT_Mint *`
- * @param tc scheduler context (unused)
- */
-void
-context_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- struct MERCHANT_Mint *mint = cls;
- long timeout;
- int max_fd;
- fd_set read_fd_set;
- fd_set write_fd_set;
- fd_set except_fd_set;
- struct GNUNET_NETWORK_FDSet *rs;
- struct GNUNET_NETWORK_FDSet *ws;
- struct GNUNET_TIME_Relative delay;
-
- mint->poller_task = NULL;
- TALER_MINT_perform (mint->ctx);
- max_fd = -1;
- timeout = -1;
- FD_ZERO (&read_fd_set);
- FD_ZERO (&write_fd_set);
- FD_ZERO (&except_fd_set);
- TALER_MINT_get_select_info (mint->ctx,
- &read_fd_set,
- &write_fd_set,
- &except_fd_set,
- &max_fd,
- &timeout);
- if (timeout >= 0)
- delay =
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS,
- timeout);
- else
- delay = GNUNET_TIME_UNIT_FOREVER_REL;
- rs = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_copy_native (rs,
- &read_fd_set,
- max_fd + 1);
- ws = GNUNET_NETWORK_fdset_create ();
- GNUNET_NETWORK_fdset_copy_native (ws,
- &write_fd_set,
- max_fd + 1);
- mint->poller_task =
- GNUNET_SCHEDULER_add_select (GNUNET_SCHEDULER_PRIORITY_DEFAULT,
- delay,
- rs,
- ws,
- &context_task,
- mint);
- GNUNET_NETWORK_fdset_destroy (rs);
- GNUNET_NETWORK_fdset_destroy (ws);
-}
-
-
-/**
* Function called whenever MHD is done with a request. If the
* request was a POST, we may have stored a `struct Buffer *` in the
* @a con_cls that might still need to be cleaned up. Call the
@@ -446,9 +318,12 @@ run_daemon (void *cls,
/**
* Kick MHD to run now, to be called after MHD_resume_connection().
+ * Basically, we need to explicitly resume MHD's event loop whenever
+ * we made progress serving a request. This function re-schedules
+ * the task processing MHD's activities to run immediately.
*/
void
-TM_trigger_daemon ()
+TMH_trigger_daemon ()
{
GNUNET_SCHEDULER_cancel (mhd_task);
run_daemon (NULL, NULL);
@@ -456,72 +331,6 @@ TM_trigger_daemon ()
/**
- * Parses mints listed in the configuration.
- *
- * @param cfg the configuration
- * @return the number of mints in the above array; #GNUNET_SYSERR upon error in
- * parsing.
- */
-static int
-parse_mints (const struct GNUNET_CONFIGURATION_Handle *cfg)
-{
- char *mints_str;
- char *token_nf; /* do no free (nf) */
- char *mint_section;
- char *mint_hostname;
- struct MERCHANT_Mint **r_mints;
- struct MERCHANT_Mint *mint;
- unsigned int cnt;
- int ok;
-
- ok = 0;
- mints_str = NULL;
- token_nf = NULL;
- mint_section = NULL;
- mint_hostname = NULL;
- r_mints = NULL;
- cnt = 0;
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- "merchant",
- "TRUSTED_MINTS",
- &mints_str));
- for (token_nf = strtok (mints_str, " ");
- NULL != token_nf;
- token_nf = strtok (NULL, " "))
- {
- GNUNET_assert (0 < GNUNET_asprintf (&mint_section,
- "mint-%s", token_nf));
- EXITIF (GNUNET_OK !=
- GNUNET_CONFIGURATION_get_value_string (cfg,
- mint_section,
- "HOSTNAME",
- &mint_hostname));
- mint = GNUNET_new (struct MERCHANT_Mint);
- mint->hostname = mint_hostname;
- GNUNET_array_append (r_mints,
- cnt,
- mint);
- GNUNET_free (mint_section);
- mint_section = NULL;
- }
- ok = 1;
-
- EXITIF_exit:
- GNUNET_free_non_null (mints_str);
- GNUNET_free_non_null (mint_section);
- GNUNET_free_non_null (mint_hostname);
- if (! ok)
- {
- GNUNET_free_non_null (r_mints);
- return GNUNET_SYSERR;
- }
- mints = r_mints;
- return cnt;
-}
-
-
-/**
* Parses auditors from the configuration.
*
* @param cfg the configuration
@@ -748,16 +557,13 @@ run (void *cls,
const char *cfgfile,
const struct GNUNET_CONFIGURATION_Handle *config)
{
- unsigned int cnt;
-
result = GNUNET_SYSERR;
shutdown_task =
GNUNET_SCHEDULER_add_delayed (GNUNET_TIME_UNIT_FOREVER_REL,
&do_shutdown,
NULL);
EXITIF (GNUNET_SYSERR ==
- (nmints =
- parse_mints (config)));
+ TMH_MINTS_parse_cfg (config));
EXITIF (GNUNET_SYSERR ==
(nauditors =
parse_auditors (config,
@@ -775,6 +581,8 @@ run (void *cls,
EXITIF (NULL ==
(privkey =
GNUNET_CRYPTO_eddsa_key_create_from_file (keyfile)));
+ GNUNET_CRYPTO_eddsa_key_get_public (privkey,
+ &pubkey.eddsa_pub);
EXITIF (NULL ==
(db_conn = TALER_MERCHANTDB_connect (config)));
EXITIF (GNUNET_OK !=
@@ -802,20 +610,6 @@ run (void *cls,
&edate_delay));
- for (cnt = 0; cnt < nmints; cnt++)
- {
- EXITIF (NULL == (mints[cnt]->ctx = TALER_MINT_init ()));
- mints[cnt]->pending = 1;
- mints[cnt]->conn = TALER_MINT_connect (mints[cnt]->ctx,
- mints[cnt]->hostname,
- &keys_mgmt_cb,
- mints[cnt],
- TALER_MINT_OPTION_END);
- EXITIF (NULL == mints[cnt]->conn);
- mints[cnt]->poller_task =
- GNUNET_SCHEDULER_add_now (&context_task,
- mints[cnt]);
- }
mhd = MHD_start_daemon (MHD_USE_SUSPEND_RESUME,
port,
diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h
index c71108c0..a66148b0 100644
--- a/src/backend/taler-merchant-httpd.h
+++ b/src/backend/taler-merchant-httpd.h
@@ -34,8 +34,6 @@
} while (0)
-
-
/**
* @brief Struct describing an URL and the handler for it.
*/
@@ -92,65 +90,6 @@ struct TMH_RequestHandler
-/**
- * Mint
- */
-struct MERCHANT_Mint
-{
- /**
- * Hostname
- */
- char *hostname;
-
- /**
- * A connection to this mint
- */
- struct TALER_MINT_Handle *conn;
-
- /**
- * This mint's context (useful to the event loop)
- */
- struct TALER_MINT_Context *ctx;
-
- /**
- * Task we use to drive the interaction with this mint.
- */
- struct GNUNET_SCHEDULER_Task *poller_task;
-
- /**
- * Flag which indicates whether some HTTP transfer between
- * this merchant and the mint is still ongoing
- */
- int pending;
-
-};
-
-
-struct MERCHANT_WIREFORMAT_Sepa
-{
- /**
- * The international bank account number
- */
- char *iban;
-
- /**
- * Name of the bank account holder
- */
- char *name;
-
- /**
- *The bank identification code
- */
- char *bic;
-
- /**
- * The latest payout date when the payment corresponding to this account has
- * to take place. A value of 0 indicates a transfer as soon as possible.
- */
- struct GNUNET_TIME_AbsoluteNBO payout;
-};
-
-
struct MERCHANT_Auditor
{
/**
@@ -161,7 +100,12 @@ struct MERCHANT_Auditor
};
-
+/**
+ * Each MHD response handler that sets the "connection_cls" to a
+ * non-NULL value must use a struct that has this struct as its first
+ * member. This struct contains a single callback, which will be
+ * invoked to clean up the memory when the contection is completed.
+ */
struct TM_HandlerContext;
/**
@@ -175,15 +119,24 @@ typedef void
(*TM_ContextCleanup)(struct TM_HandlerContext *hc);
+/**
+ * Each MHD response handler that sets the "connection_cls" to a
+ * non-NULL value must use a struct that has this struct as its first
+ * member. This struct contains a single callback, which will be
+ * invoked to clean up the memory when the contection is completed.
+ */
struct TM_HandlerContext
{
+ /**
+ * Function to execute the handler-specific cleanup of the
+ * (typically larger) context.
+ */
TM_ContextCleanup cc;
};
-
/**
* Our wire format details in JSON format (with salt).
*/
@@ -195,48 +148,33 @@ extern json_t *j_wire;
extern struct GNUNET_HashCode h_wire;
-
-extern struct MERCHANT_Auditor *auditors;
-extern unsigned int nauditors;
-
-extern struct MERCHANT_Mint **mints;
-
extern struct GNUNET_CRYPTO_EddsaPrivateKey *privkey;
+extern struct TALER_MerchantPublicKeyP pubkey;
-extern PGconn *db_conn;
+extern struct MERCHANT_Auditor *auditors;
-extern unsigned int nmints;
+extern unsigned int nauditors;
-extern struct GNUNET_TIME_Relative edate_delay;
-void
-context_task (void *cls,
- const struct GNUNET_SCHEDULER_TaskContext *tc);
+extern PGconn *db_conn;
-/**
- * Take the global wire details and return a JSON containing them,
- * compliantly with the Taler's API.
- *
- * @param wire the merchant's wire details
- * @param salt the nounce for hashing the wire details with
- * @return JSON representation of the wire details, NULL upon errors
- */
-json_t *
-MERCHANT_get_wire_json (const struct MERCHANT_WIREFORMAT_Sepa *wire,
- uint64_t salt);
+extern struct GNUNET_TIME_Relative edate_delay;
/**
* Kick MHD to run now, to be called after MHD_resume_connection().
+ * Basically, we need to explicitly resume MHD's event loop whenever
+ * we made progress serving a request. This function re-schedules
+ * the task processing MHD's activities to run immediately.
*/
void
-TM_trigger_daemon (void);
+TMH_trigger_daemon (void);
#endif
diff --git a/src/backend/taler-merchant-httpd_contract.c b/src/backend/taler-merchant-httpd_contract.c
index b5050666..81bf3ed9 100644
--- a/src/backend/taler-merchant-httpd_contract.c
+++ b/src/backend/taler-merchant-httpd_contract.c
@@ -31,6 +31,7 @@
#include <taler/taler_mint_service.h>
#include "taler-merchant-httpd.h"
#include "taler-merchant-httpd_parsing.h"
+#include "taler-merchant-httpd_mints.h"
#include "taler-merchant-httpd_responses.h"
#include "taler_merchantdb_lib.h"
#include "taler-merchant-httpd.h"
@@ -57,11 +58,8 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
size_t *upload_data_size)
{
json_t *root;
- json_t *trusted_mints;
json_t *j_auditors;
json_t *auditor;
- json_t *mint;
- const struct TALER_MINT_Keys *keys;
int res;
int cnt;
struct GNUNET_HashCode h_wire;
@@ -82,21 +80,6 @@ MH_handler_contract (struct TMH_RequestHandler *rh,
/* Generate preferred mint(s) array. */
- trusted_mints = json_array ();
- for (cnt = 0; cnt < nmints; cnt++)
- {
- if (! mints[cnt]->pending)
- {
- keys = TALER_MINT_get_keys (mints[cnt]->conn);
- mint = json_pack ("{s:s, s:o}",
- "url", mints[cnt]->hostname,
- "master_pub",
- TALER_json_from_data
- (&keys->master_pub.eddsa_pub,
- sizeof (keys->master_pub.eddsa_pub)));
- json_array_append_new (trusted_mints, mint);
- }
- }
j_auditors = json_array ();
for (cnt = 0; cnt < nauditors; cnt++)
{
diff --git a/src/backend/taler-merchant-httpd_pay.c b/src/backend/taler-merchant-httpd_pay.c
index 23aa178d..a8d85cc2 100644
--- a/src/backend/taler-merchant-httpd_pay.c
+++ b/src/backend/taler-merchant-httpd_pay.c
@@ -32,29 +32,65 @@
#include "taler-merchant-httpd_parsing.h"
#include "taler-merchant-httpd_responses.h"
#include "taler-merchant-httpd_mhd.h"
+#include "taler-merchant-httpd_mints.h"
#include "taler_merchantdb_lib.h"
/**
- * Outcome of a /deposit request for a coin. Typically forming an array enclosed
- * into the unique PayContext
+ * Information we keep for an individual call to the /pay handler.
+ */
+struct PayContext;
+
+
+/**
+ * Information kept during a /pay request for each coin.
*/
struct MERCHANT_DepositConfirmation
{
+
+ /**
+ * Reference to the main PayContext
+ */
+ struct PayContext *pc;
+
/**
- * Reference to the per-deposit-handler Context. Needed by the
- * cleanup function to get it freed
+ *
*/
- struct DepositCallbackContext *dcc;
+ struct TALER_MINT_DepositHandle *dh;
/**
* The mint's response body (JSON). Mainly useful in case
* some callback needs to send back to the to the wallet the
- * outcome of an erroneous coin
+ * outcome of an erroneous coin. DEAD?
*/
json_t *proof;
/**
+ * Denomination for this coin.
+ */
+ struct TALER_DenominationPublicKey denom;
+
+ /**
+ *
+ */
+ struct TALER_Amount percoin_amount;
+
+ /**
+ *
+ */
+ struct TALER_CoinSpendPublicKeyP coin_pub;
+
+ /**
+ *
+ */
+ struct TALER_DenominationSignature ub_sig;
+
+ /**
+ *
+ */
+ struct TALER_CoinSpendSignatureP coin_sig;
+
+ /**
* True if this coin's outcome has been read from
* its cb
*/
@@ -65,61 +101,12 @@ struct MERCHANT_DepositConfirmation
*/
unsigned int exit_status;
-};
-
-
-
-/**
- * Fetch the deposit fee related to the given coin aggregate.
- * @param connection the connection to send an error response to
- * @param coin_aggregate a coin "aggregate" is the JSON set of
- * values contained in a single cell of the 'coins' array sent
- * in a payment
- * @param deposit_fee where to store the resulting deposit fee
- * @param mint_index the index which points the chosen mint within
- * the global 'mints' array
- * @return GNUNET_OK if successful, GNUNET_NO if the data supplied
- * is invalid (including the case when the key is not found),
- * GNUNET_SYSERR upon internal errors
- */
-static int
-deposit_fee_from_coin_aggregate (struct MHD_Connection *connection,
- json_t *coin_aggregate,
- struct TALER_Amount *deposit_fee,
- unsigned int mint_index)
-{
- int res;
- const struct TALER_MINT_Keys *keys;
- const struct TALER_MINT_DenomPublicKey *denom_details;
- struct TALER_DenominationPublicKey denom;
+ /**
+ * Offset of this coin into the array of all coins outcomes
+ */
+ unsigned int index;
- struct TMH_PARSE_FieldSpecification spec[] = {
- TMH_PARSE_member_denomination_public_key ("denom_pub", &denom),
- TMH_PARSE_MEMBER_END
- };
-
- res = TMH_PARSE_json_data (connection,
- coin_aggregate,
- spec);
- if (GNUNET_OK != res)
- return res; /* may return GNUNET_NO */
-
- if (1 == mints[mint_index]->pending)
- return GNUNET_SYSERR;
- keys = TALER_MINT_get_keys (mints[mint_index]->conn);
- denom_details = TALER_MINT_get_denomination_key (keys, &denom);
- if (NULL == denom_details)
- {
- TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_BAD_REQUEST,
- "{s:s, s:o}",
- "hint", "unknown denom to mint",
- "denom_pub", TALER_json_from_rsa_public_key (denom.rsa_public_key));
- return GNUNET_NO;
- }
- *deposit_fee = denom_details->fee_deposit;
- return GNUNET_OK;
-}
+};
/**
@@ -149,132 +136,260 @@ struct PayContext
void *json_parse_context;
/**
- * Response to return, NULL if we don't have one yet.
+ * Root node of the request body in JSON.
*/
- struct MHD_Response *response;
+ json_t *root;
/**
- * Transaction id
+ * Coins included in @e root.
+ */
+ json_t *coins;
+
+ /**
+ * Mint URI given in @e root.
+ */
+ char *chosen_mint;
+
+ /**
+ * Transaction ID given in @e root.
*/
uint64_t transaction_id;
/**
- * How many coins this paymen is made of.
+ * Maximum fee from @e root.
*/
- unsigned int coins_cnt;
+ struct TALER_Amount max_fee;
/**
- * HTTP status code to use for the reply, i.e 200 for "OK".
- * Special value UINT_MAX is used to indicate hard errors
- * (no reply, return MHD_NO).
+ * Amount from @e root.
*/
- unsigned int response_code;
+ struct TALER_Amount amount;
-};
+ /**
+ * Timestamp from @e root.
+ */
+ struct GNUNET_TIME_Absolute timestamp;
-/**
- * Information needed by a single /deposit callback to refer to its
- * own coin inside the confirmations array, namely `struct MERCHANT_DepositConfirmation *dc`
- * above. Note: this information can NOT be shared between all the callbacks.
- */
-struct DepositCallbackContext
-{
+ /**
+ * Refund deadline from @e root.
+ */
+ struct GNUNET_TIME_Absolute refund_deadline;
/**
- * Offset of this coin into the array of all coins outcomes
+ * "H_contract" from @e root.
*/
- unsigned int index;
+ struct GNUNET_HashCode h_contract;
/**
- * Reference to the main PayContext
+ *
*/
- struct PayContext *pc;
+ struct GNUNET_TIME_Absolute edate;
+
+ /**
+ * Response to return, NULL if we don't have one yet.
+ */
+ struct MHD_Response *response;
+
+ /**
+ * Number of coins this payment is made of.
+ */
+ unsigned int coins_cnt;
+
+ /**
+ * Number of transactions still pending.
+ */
+ unsigned int pending;
+
+ /**
+ * HTTP status code to use for the reply, i.e 200 for "OK".
+ * Special value UINT_MAX is used to indicate hard errors
+ * (no reply, return MHD_NO).
+ */
+ unsigned int response_code;
};
+
/**
* Callback to handle a deposit permission's response.
*
- * @param cls see `struct MERCHANT_DepositConfirmationCls` (i.e. a poinetr to the global
- * array of confirmations and an index for this call in that array). That way, the last
- * executed callback can detect that no other confirmations are on the way, and can pack
- * a response for the wallet
- * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful deposit;
- * 0 if the mint's reply is bogus (fails to follow the protocol)
- * @param proof the received JSON reply, should be kept as proof (and, in case of errors,
- * be forwarded to the customer)
+ * @param cls a `struct MERCHANT_DepositConfirmation` (i.e. a pointer
+ * into the global array of confirmations and an index for this call
+ * in that array). That way, the last executed callback can detect
+ * that no other confirmations are on the way, and can pack a response
+ * for the wallet
+ * @param http_status HTTP response code, #MHD_HTTP_OK
+ * (200) for successful deposit; 0 if the mint's reply is bogus (fails
+ * to follow the protocol)
+ * @param proof the received JSON reply,
+ * should be kept as proof (and, in case of errors, be forwarded to
+ * the customer)
*/
static void
deposit_cb (void *cls,
unsigned int http_status,
json_t *proof)
{
- struct DepositCallbackContext *dcc = cls;
- int i;
-
- /*FIXME the index is the same for every individual cb */
- if (GNUNET_SYSERR ==
- TALER_MERCHANTDB_deposit_permission_update (db_conn,
- dcc->pc->transaction_id,
- 0))
- /* TODO */
- printf ("db error\n");
- dcc->pc->dc[dcc->index].ackd = 1;
- dcc->pc->dc[dcc->index].exit_status = http_status;
- dcc->pc->dc[dcc->index].proof = proof;
-
- /* loop through the confirmation array and return accordingly */
- for (i = 0; i < dcc->pc->coins_cnt; i++)
- {
- /* just return if there is at least one coin to be still
- confirmed */
- if (! dcc->pc->dc[i].ackd)
- {
- printf ("still vacant coins\n");
- return;
- }
- }
-
- printf ("All /deposit(s) ack'd\n");
-
- dcc->pc->response = MHD_create_response_from_buffer (strlen ("All coins ack'd by the mint\n"),
- "All coins ack'd by the mint\n",
- MHD_RESPMEM_MUST_COPY);
- dcc->pc->response_code = MHD_HTTP_OK;
- /* Clean up what we can already */
- MHD_resume_connection (dcc->pc->connection);
- TM_trigger_daemon (); /* we resumed, kick MHD */
+ struct MERCHANT_DepositConfirmation *dc = cls;
+ struct PayContext *pc = dc->pc;
+
+ /* FIXME: store to DB! */
+ dc->ackd = 1;
+ dc->exit_status = http_status;
+ dc->proof = proof; /* FIXME: needs rc+1 */
+ pc->pending--;
+ if (0 != pc->pending)
+ return; /* still more to do */
+
+ pc->response
+ = MHD_create_response_from_buffer (strlen ("All coins ack'd by the mint\n"),
+ "All coins ack'd by the mint\n",
+ MHD_RESPMEM_MUST_COPY);
+ /* FIXME: move this logic into a function: */
+ pc->response_code = MHD_HTTP_OK;
+ MHD_resume_connection (pc->connection);
+ TMH_trigger_daemon (); /* we resumed, kick MHD */
}
+/**
+ * Custom cleanup routine for a `struct PayContext`.
+ *
+ * @param hc the `struct PayContext` to clean up.
+ */
static void
pay_context_cleanup (struct TM_HandlerContext *hc)
{
- int i;
struct PayContext *pc = (struct PayContext *) hc;
-
- for (i = 0; i < pc->coins_cnt; i++)
- GNUNET_free_non_null (pc->dc[i].dcc);
+ unsigned int i;
TMH_PARSE_post_cleanup_callback (pc->json_parse_context);
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[i];
- #if 0
+ /* FIXME: clean up 'dc'! */
+ }
+ GNUNET_free_non_null (pc->dc);
if (NULL != pc->response)
{
- /* undestroyable regardless of the other MHD_destroy_response called
- in this source, FIXME */
-
MHD_destroy_response (pc->response);
pc->response = NULL;
}
- #endif
-
- GNUNET_free_non_null (pc->dc);
GNUNET_free (pc);
}
/**
+ * Function called with the result of our mint lookup.
+ *
+ * @param cls the `struct PayContext`
+ * @param mint NULL if mint was not found to be acceptable
+ * @param mh NULL if mint was not found to be acceptable
+ */
+static void
+process_pay_with_mint (void *cls,
+ struct MERCHANT_Mint *mint,
+ struct TALER_MINT_Handle *mh)
+{
+ struct PayContext *pc = cls;
+ struct TALER_Amount acc_fee;
+ struct TALER_Amount coin_fee;
+ const struct TALER_MINT_Keys *keys;
+ unsigned int i;
+
+ if (NULL == mint)
+ {
+ /* The mint on offer is not in the set of our (trusted)
+ mints. Reject the payment. */
+ MHD_resume_connection (pc->connection);
+ pc->response_code = 403;
+ pc->response = TMH_RESPONSE_make_external_error ("unknown mint");
+ TMH_trigger_daemon ();
+ return;
+ }
+
+ keys = TALER_MINT_get_keys (mh);
+ if (NULL == keys)
+ {
+ GNUNET_break (0);
+ pc->response_code = UINT_MAX;
+ pc->response = TMH_RESPONSE_make_internal_error ("no keys");
+ TMH_trigger_daemon ();
+ return;
+ }
+
+ /* FIXME: do not just total up the fees, but also
+ the value of the deposited coins! */
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[i];
+ const struct TALER_MINT_DenomPublicKey *denom_details;
+
+ denom_details = TALER_MINT_get_denomination_key (keys,
+ &dc->denom);
+ if (NULL == denom_details)
+ {
+ pc->response_code = MHD_HTTP_BAD_REQUEST;
+ pc->response
+ = TMH_RESPONSE_make_json_pack ("{s:s, s:o}",
+ "hint", "unknown denom to mint",
+ "denom_pub", TALER_json_from_rsa_public_key (dc->denom.rsa_public_key));
+ TMH_trigger_daemon ();
+ return;
+ }
+ if (0 == i)
+ acc_fee = denom_details->fee_deposit;
+ else
+ TALER_amount_add (&acc_fee,
+ &denom_details->fee_deposit,
+ &coin_fee);
+ }
+
+ if (-1 == TALER_amount_cmp (&pc->max_fee,
+ &acc_fee))
+ {
+ pc->response_code = MHD_HTTP_NOT_ACCEPTABLE;
+ pc->response = TMH_RESPONSE_make_external_error ("fees too high");
+ TMH_trigger_daemon ();
+ return;
+ }
+
+ /* Initiate /deposit operation for all coins */
+ for (i=0;i<pc->coins_cnt;i++)
+ {
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[i];
+
+ dc->dh = TALER_MINT_deposit (mh,
+ &dc->percoin_amount,
+ pc->edate,
+ j_wire,
+ &pc->h_contract,
+ &dc->coin_pub,
+ &dc->ub_sig,
+ &dc->denom,
+ pc->timestamp,
+ pc->transaction_id,
+ &pubkey,
+ pc->refund_deadline,
+ &dc->coin_sig,
+ &deposit_cb,
+ dc);
+ if (NULL == dc->dh)
+ {
+ MHD_resume_connection (pc->connection);
+ pc->response_code = MHD_HTTP_SERVICE_UNAVAILABLE;
+ pc->response = TMH_RESPONSE_make_json_pack ("{s:s, s:i}",
+ "mint", pc->chosen_mint,
+ "transaction_id", pc->transaction_id);
+ TMH_trigger_daemon ();
+ return;
+ }
+ }
+}
+
+
+/**
* Accomplish this payment.
*
* @param rh context of the handler
@@ -294,52 +409,9 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
size_t *upload_data_size)
{
struct PayContext *pc;
- json_t *root;
- json_t *coins;
- char *chosen_mint;
- json_t *coin_aggregate;
- unsigned int mint_index; /*a cell in the global array*/
- unsigned int coins_index;
- unsigned int coins_cnt;
- uint64_t transaction_id;
int res;
- struct TALER_MINT_DepositHandle *dh;
- struct TALER_Amount max_fee;
- struct TALER_Amount acc_fee;
- struct TALER_Amount coin_fee;
- struct TALER_Amount amount;
- struct TALER_Amount percoin_amount;
- struct GNUNET_TIME_Absolute edate;
- struct GNUNET_TIME_Absolute timestamp;
- struct GNUNET_TIME_Absolute refund_deadline;
- struct TALER_MerchantPublicKeyP pubkey;
- struct TALER_CoinSpendPublicKeyP coin_pub;
- struct TALER_DenominationPublicKey denom_pub;
- struct TALER_DenominationSignature ub_sig;
- struct TALER_CoinSpendSignatureP coin_sig;
- struct GNUNET_HashCode h_contract;
- struct MERCHANT_DepositConfirmation *dc;
-
- struct TMH_PARSE_FieldSpecification spec[] = {
- TMH_PARSE_member_array ("coins", &coins),
- TMH_PARSE_member_string ("mint", &chosen_mint),
- TMH_PARSE_member_amount ("max_fee", &max_fee),
- TMH_PARSE_member_amount ("amount", &amount),
- TMH_PARSE_member_time_abs ("timestamp", &timestamp),
- TMH_PARSE_member_time_abs ("refund_deadline", &refund_deadline),
- TMH_PARSE_member_uint64 ("transaction_id", &transaction_id),
- TMH_PARSE_member_fixed ("H_contract", &h_contract),
- TMH_PARSE_MEMBER_END
- };
-
- struct TMH_PARSE_FieldSpecification coin_aggregate_spec[] = {
- TMH_PARSE_member_amount ("f", &percoin_amount),
- TMH_PARSE_member_fixed ("coin_pub", &coin_pub.eddsa_pub),
- TMH_PARSE_member_denomination_public_key ("denom_pub", &denom_pub),
- TMH_PARSE_member_denomination_signature ("ub_sig", &ub_sig),
- TMH_PARSE_member_fixed ("coin_sig", &coin_sig.eddsa_signature),
- TMH_PARSE_MEMBER_END
- };
+ json_t *coin;
+ unsigned int coins_index;
if (NULL == *connection_cls)
{
@@ -355,21 +427,17 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
}
if (0 != pc->response_code)
{
+ /* We are *done* processing the request, just queue the response (!) */
if (UINT_MAX == pc->response_code)
return MHD_NO; /* hard error */
- /* We are *done* processing the request, just queue the response (!) */
res = MHD_queue_response (connection,
pc->response_code,
pc->response);
- #if 0
- if (pc->response != NULL)
+ if (NULL != pc->response)
{
- /* undestroyable regardless of the other MHD_destroy_response called
- in this source, FIXME */
MHD_destroy_response (pc->response);
pc->response = NULL;
}
- #endif
return res;
}
@@ -377,202 +445,81 @@ MH_handler_pay (struct TMH_RequestHandler *rh,
&pc->json_parse_context,
upload_data,
upload_data_size,
- &root);
+ &pc->root);
if (GNUNET_SYSERR == res)
- return MHD_NO;
- /* the POST's body has to be further fetched */
- if ((GNUNET_NO == res) || (NULL == root))
- return MHD_YES;
-
- res = TMH_PARSE_json_data (connection,
- root,
- spec);
+ return MHD_NO; /* error parsing JSON */
+ if ((GNUNET_NO == res) || (NULL == pc->root))
+ return MHD_YES; /* the POST's body has to be further fetched */
- if (GNUNET_YES != res)
- return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
-
- /* 1 Check if the chosen mint is among the merchant's preferred.
-
- An error in this case could be due to:
-
- * the wallet indicated a non existent mint
- * the wallet indicated a non trusted mint
-
- NOTE: by preventively checking this, the merchant
- avoids getting HTTP response codes from random
- websites that may mislead the wallet in the way
- of managing the error. Of course, that protect the
- merchant from POSTing coins to untrusted mints.
-
- */
+ /* Got the JSON upload, parse it (FIXME: all of this here?) */
- for (mint_index = 0; mint_index <= nmints; mint_index++)
+ /* We got no 'edate' from frontend. Generate it here; it will be
+ timestamp plus the edate_delay supplied in config file */
+ if (NULL == json_object_get (pc->root, "edate"))
{
- /* no mint found in array */
- if (mint_index == nmints)
- {
- mint_index = -1;
- break;
- }
-
- /* test it by checking public key */
- if (0 == strcmp (mints[mint_index]->hostname,
- chosen_mint))
- break;
-
- }
-
- if (-1 == mint_index)
- return TMH_RESPONSE_reply_external_error (connection, "unknown mint");
-
- /* no 'edate' from frontend. Generate it here; it will be timestamp
- + a edate delay supplied in config file */
- if (NULL == json_object_get (root, "edate"))
- {
- edate = GNUNET_TIME_absolute_add (timestamp, edate_delay);
- if (-1 == json_object_set (root, "edate", TALER_json_from_abs (edate)))
+ pc->edate = GNUNET_TIME_absolute_add (pc->timestamp, // FIXME: uninit!
+ edate_delay);
+ if (-1 == json_object_set (pc->root,
+ "edate",
+ TALER_json_from_abs (pc->edate)))
return MHD_NO;
}
- coins_cnt = json_array_size (coins);
-
- if (0 == coins_cnt)
- return TMH_RESPONSE_reply_external_error (connection, "no coins given");
-
- json_array_foreach (coins, coins_index, coin_aggregate)
{
- res = deposit_fee_from_coin_aggregate (connection,
- coin_aggregate,
- &coin_fee,
- mint_index);
- if (GNUNET_NO == res)
- return MHD_YES;
- if (GNUNET_SYSERR == res)
- return MHD_NO;
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_array ("coins", &pc->coins),
+ TMH_PARSE_member_string ("mint", &pc->chosen_mint),
+ TMH_PARSE_member_amount ("max_fee", &pc->max_fee),
+ TMH_PARSE_member_amount ("amount", &pc->amount),
+ TMH_PARSE_member_time_abs ("edate", &pc->edate),
+ TMH_PARSE_member_time_abs ("timestamp", &pc->timestamp),
+ TMH_PARSE_member_time_abs ("refund_deadline", &pc->refund_deadline),
+ TMH_PARSE_member_uint64 ("transaction_id", &pc->transaction_id),
+ TMH_PARSE_member_fixed ("H_contract", &pc->h_contract),
+ TMH_PARSE_MEMBER_END
+ };
- if (0 == coins_index)
- acc_fee = coin_fee;
- else
- TALER_amount_add (&acc_fee,
- &acc_fee,
- &coin_fee);
+ res = TMH_PARSE_json_data (connection,
+ pc->root,
+ spec);
+ if (GNUNET_YES != res)
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+
+ if (0 == json_array_size (pc->coins))
+ return TMH_RESPONSE_reply_external_error (connection,
+ "no coins given");
}
+ pc->dc = GNUNET_new_array (json_array_size (pc->coins),
+ struct MERCHANT_DepositConfirmation);
-
- if (-1 == TALER_amount_cmp (&max_fee, &acc_fee))
- return MHD_HTTP_NOT_ACCEPTABLE;
-
- /* cutting off unneeded fields from deposit permission as
- gotten from the wallet */
- if (-1 == json_object_del (root, "mint"))
- return TMH_RESPONSE_reply_external_error (connection,
- "malformed/non-existent 'mint' field");
- if (-1 == json_object_del (root, "coins"))
- return TMH_RESPONSE_reply_external_error (connection,
- "malformed/non-existent 'coins' field");
-
- /* adding our public key to deposit permission */
- GNUNET_CRYPTO_eddsa_key_get_public (privkey, &pubkey.eddsa_pub);
- json_object_set_new (root,
- "merchant_pub",
- TALER_json_from_data (&pubkey, sizeof (pubkey)));
-
- /* since memory is zero'd out by GNUNET_malloc, any 'ackd' field will be
- (implicitly) set to false */
- dc = GNUNET_malloc (coins_cnt * sizeof (struct MERCHANT_DepositConfirmation));
- if (NULL == dc)
- return TMH_RESPONSE_reply_internal_error (connection, "memory failure");
-
- /* DEBUG CHECKPOINT: return a provisory fullfilment page to the wallet
- to test the reception of coins array */
-
- #ifdef COINSCHECKPOINT
- rh->data = "Coins received\n";
- return TMH_MHD_handler_static_response (rh,
- connection,
- connection_cls,
- upload_data,
- upload_data_size);
-
- #endif
-
- /* suspend connection until the last coin has been ack'd to the cb.
- That last cb will finally resume the connection and send back a response */
- MHD_suspend_connection (connection);
-
- pc->dc = dc;
- pc->coins_cnt = coins_cnt;
- pc->transaction_id = transaction_id;
-
-
- json_array_foreach (coins, coins_index, coin_aggregate)
+ json_array_foreach (pc->coins, coins_index, coin)
{
+ struct MERCHANT_DepositConfirmation *dc = &pc->dc[coins_index];
+ struct TMH_PARSE_FieldSpecification spec[] = {
+ TMH_PARSE_member_denomination_public_key ("denom_pub", &dc->denom),
+ TMH_PARSE_member_amount ("f", &dc->percoin_amount),
+ TMH_PARSE_member_fixed ("coin_pub", &dc->coin_pub),
+ TMH_PARSE_member_denomination_signature ("ub_sig", &dc->ub_sig),
+ TMH_PARSE_member_fixed ("coin_sig", &dc->coin_sig),
+ TMH_PARSE_MEMBER_END
+ };
- /* 3 For each coin in DB
-
- a. Generate a deposit permission
- b. store it in DB
- c. POST to the mint (see mint-lib for this)
- (retry until getting a persisten state)
- */
-
- /* a */
- if (-1 == json_object_update (root, coin_aggregate))
- return TMH_RESPONSE_reply_internal_error (connection,
- "deposit permission not generated for storing");
-
- /* b */
- char *deposit_permission_str = json_dumps (root, JSON_COMPACT);
- if (GNUNET_OK != TALER_MERCHANTDB_deposit_permission_store (db_conn,
- deposit_permission_str,
- transaction_id,
- 1,
- mints[mint_index]->hostname))
- return TMH_RESPONSE_reply_internal_error (connection, "internal DB failure");
res = TMH_PARSE_json_data (connection,
- coin_aggregate,
- coin_aggregate_spec);
- if (GNUNET_OK != res)
- return res; /* may return GNUNET_NO */
-
- /* c */
- struct DepositCallbackContext *percoin_dcc = GNUNET_new (struct DepositCallbackContext);
- pc->dc[coins_index].dcc = percoin_dcc;
- percoin_dcc->index = coins_index;
- percoin_dcc->pc = pc;
-
- dh = TALER_MINT_deposit (mints[mint_index]->conn,
- &percoin_amount,
- edate,
- j_wire,
- &h_contract,
- &coin_pub,
- &ub_sig,
- &denom_pub,
- timestamp,
- transaction_id,
- &pubkey,
- refund_deadline,
- &coin_sig,
- &deposit_cb,
- percoin_dcc); /*FIXME TODO instantiate an individual cls for each
- cb: each of them needs an index which points the
- array of all the confirmations */
- if (NULL == dh)
- {
- MHD_resume_connection (connection);
- return TMH_RESPONSE_reply_json_pack (connection,
- MHD_HTTP_SERVICE_UNAVAILABLE,
- "{s:s, s:i}",
- "mint", mints[mint_index]->hostname,
- "transaction_id", transaction_id);
- }
+ coin,
+ spec);
+ if (GNUNET_YES != res)
+ return (GNUNET_NO == res) ? MHD_YES : MHD_NO;
+
+
}
- GNUNET_SCHEDULER_cancel (mints[mint_index]->poller_task);
- GNUNET_SCHEDULER_add_now (context_task, mints[mint_index]->ctx);
+ /* Find the responsible mint, this may take a while... */
+ TMH_MINTS_find_mint (pc->chosen_mint,
+ &process_pay_with_mint,
+ pc);
+ /* Suspend connection until the last coin has been ack'd or
+ until we have encountered a hard error.
+ Eventually, we will resume the connection and send back a response. */
+ MHD_suspend_connection (connection);
return MHD_YES;
-
- /* 4 Return response code: success, or whatever data the
- mint sent back regarding some bad coin */
}
diff --git a/src/backend/taler-merchant-httpd_responses.c b/src/backend/taler-merchant-httpd_responses.c
index 1e2902c2..d8ba1170 100644
--- a/src/backend/taler-merchant-httpd_responses.c
+++ b/src/backend/taler-merchant-httpd_responses.c
@@ -29,21 +29,16 @@
/**
- * Send JSON object as response.
+ * Make JSON response object.
*
- * @param connection the MHD connection
* @param json the json object
- * @param response_code the http response code
- * @return MHD result code
+ * @return MHD response object
*/
-int
-TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
- const json_t *json,
- unsigned int response_code)
+struct MHD_Response *
+TMH_RESPONSE_make_json (const json_t *json)
{
struct MHD_Response *resp;
char *json_str;
- int ret;
json_str = json_dumps (json, JSON_INDENT(2));
GNUNET_assert (NULL != json_str);
@@ -53,11 +48,34 @@ TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
{
free (json_str);
GNUNET_break (0);
- return MHD_NO;
+ return NULL;
}
(void) MHD_add_response_header (resp,
MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json");
+ return resp;
+}
+
+
+/**
+ * Send JSON object as response.
+ *
+ * @param connection the MHD connection
+ * @param json the json object
+ * @param response_code the http response code
+ * @return MHD result code
+ */
+int
+TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
+ const json_t *json,
+ unsigned int response_code)
+{
+ struct MHD_Response *resp;
+ int ret;
+
+ resp = TMH_RESPONSE_make_json (json);
+ if (NULL == resp)
+ return MHD_NO;
ret = MHD_queue_response (connection,
response_code,
resp);
@@ -67,6 +85,40 @@ TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
/**
+ * Make JSON response object.
+ *
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_json_pack (const char *fmt,
+ ...)
+{
+ json_t *json;
+ va_list argp;
+ struct MHD_Response *ret;
+ json_error_t jerror;
+
+ va_start (argp, fmt);
+ json = json_vpack_ex (&jerror, 0, fmt, argp);
+ va_end (argp);
+ if (NULL == json)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to pack JSON with format `%s': %s\n",
+ fmt,
+ jerror.text);
+ GNUNET_break (0);
+ return MHD_NO;
+ }
+ ret = TMH_RESPONSE_make_json (json);
+ json_decref (json);
+ return ret;
+}
+
+
+/**
* Function to call to handle the request by building a JSON
* reply from a format string and varargs.
*
@@ -106,6 +158,22 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection,
return ret;
}
+
+/**
+ * Create a response indicating an internal error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_internal_error (const char *hint)
+{
+ return TMH_RESPONSE_make_json_pack ("{s:s, s:s}",
+ "error", "internal error",
+ "hint", hint);
+}
+
+
/**
* Send a response indicating an internal error.
*
@@ -124,6 +192,7 @@ TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
"hint", hint);
}
+
/**
* Send a response indicating that the request was too big.
*
@@ -186,6 +255,7 @@ TMH_RESPONSE_add_global_headers (struct MHD_Response *response)
"close");
}
+
/**
* Send a response indicating an external error.
*
@@ -203,4 +273,21 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
"error", "client error",
"hint", hint);
}
+
+
+/**
+ * Create a response indicating an external error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_external_error (const char *hint)
+{
+ return TMH_RESPONSE_make_json_pack ("{s:s, s:s}",
+ "error", "client error",
+ "hint", hint);
+}
+
+
/* end of taler-mint-httpd_responses.c */
diff --git a/src/backend/taler-merchant-httpd_responses.h b/src/backend/taler-merchant-httpd_responses.h
index ad063eb8..7240d601 100644
--- a/src/backend/taler-merchant-httpd_responses.h
+++ b/src/backend/taler-merchant-httpd_responses.h
@@ -31,6 +31,16 @@
#include <pthread.h>
/**
+ * Make JSON response object.
+ *
+ * @param json the json object
+ * @return MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_json (const json_t *json);
+
+
+/**
* Send JSON object as response.
*
* @param connection the MHD connection
@@ -45,6 +55,18 @@ TMH_RESPONSE_reply_json (struct MHD_Connection *connection,
/**
+ * Make JSON response object.
+ *
+ * @param fmt format string for pack
+ * @param ... varargs
+ * @return MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_json_pack (const char *fmt,
+ ...);
+
+
+/**
* Function to call to handle the request by building a JSON
* reply from a format string and varargs.
*
@@ -70,6 +92,7 @@ TMH_RESPONSE_reply_json_pack (struct MHD_Connection *connection,
int
TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection);
+
/**
* Send a response indicating an internal error.
*
@@ -80,6 +103,18 @@ TMH_RESPONSE_reply_invalid_json (struct MHD_Connection *connection);
int
TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
const char *hint);
+
+
+/**
+ * Create a response indicating an internal error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_internal_error (const char *hint);
+
+
/**
* Send a response indicating an external error.
*
@@ -90,6 +125,18 @@ TMH_RESPONSE_reply_internal_error (struct MHD_Connection *connection,
int
TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
const char *hint);
+
+
+/**
+ * Create a response indicating an external error.
+ *
+ * @param hint hint about the internal error's nature
+ * @return a MHD response object
+ */
+struct MHD_Response *
+TMH_RESPONSE_make_external_error (const char *hint);
+
+
/**
* Send a response indicating that the request was too big.
*
@@ -99,6 +146,7 @@ TMH_RESPONSE_reply_external_error (struct MHD_Connection *connection,
int
TMH_RESPONSE_reply_request_too_large (struct MHD_Connection *connection);
+
/**
* Add headers we want to return in every response.
* Useful for testing, like if we want to always close