aboutsummaryrefslogtreecommitdiff
path: root/src/mint-lib
diff options
context:
space:
mode:
Diffstat (limited to 'src/mint-lib')
-rw-r--r--src/mint-lib/Makefile.am4
-rw-r--r--src/mint-lib/mint_api.c1023
-rw-r--r--src/mint-lib/mint_api_context.c370
-rw-r--r--src/mint-lib/mint_api_context.h83
-rw-r--r--src/mint-lib/mint_api_handle.c821
-rw-r--r--src/mint-lib/mint_api_handle.h59
-rw-r--r--src/mint-lib/test_mint_api.c2
7 files changed, 1352 insertions, 1010 deletions
diff --git a/src/mint-lib/Makefile.am b/src/mint-lib/Makefile.am
index e6e25b046..d5940e9c2 100644
--- a/src/mint-lib/Makefile.am
+++ b/src/mint-lib/Makefile.am
@@ -14,7 +14,8 @@ libtalermint_la_LDFLAGS = \
-no-undefined
libtalermint_la_SOURCES = \
- mint_api.c
+ mint_api_context.c mint_api_context.h \
+ mint_api_handle.c mint_api_handle.h
libtalermint_la_LIBADD = \
-lgnunetutil \
@@ -32,4 +33,3 @@ test_mint_api_LDADD = \
$(top_builddir)/src/util/libtalerutil.la \
-lgnunetutil \
-ljansson
-
diff --git a/src/mint-lib/mint_api.c b/src/mint-lib/mint_api.c
index 250d52ec1..23d591ff1 100644
--- a/src/mint-lib/mint_api.c
+++ b/src/mint-lib/mint_api.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2014 Christian Grothoff (and other contributing authors)
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
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
@@ -14,7 +14,6 @@
TALER; see the file COPYING. If not, If not, see
<http://www.gnu.org/licenses/>
*/
-
/**
* @file mint-lib/mint_api.c
* @brief Implementation of the client interface to mint's HTTP API
@@ -28,10 +27,18 @@
#include "taler_signatures.h"
-#define CURL_STRERROR(TYPE, FUNCTION, CODE) \
- GNUNET_log (TYPE, "cURL function `%s' has failed at `%s:%d' with error: %s", \
- FUNCTION, __FILE__, __LINE__, curl_easy_strerror (CODE));
+// leftovers follow...
+/**
+ * Log error related to CURL operations.
+ *
+ * @param type log level
+ * @param function which function failed to run
+ * @param code what was the curl error code
+ */
+#define CURL_STRERROR(type, function, code) \
+ GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \
+ function, __FILE__, __LINE__, curl_easy_strerror (code));
/**
@@ -43,150 +50,10 @@
__FILE__, __LINE__, error.text, error.source)
/**
- * Failsafe flag
- */
-static int fail;
-
-/**
- * Context
+ * Failsafe flag. Raised if our constructor fails to initialize
+ * the Curl library.
*/
-struct TALER_MINT_Context
-{
- /**
- * CURL multi handle
- */
- CURLM *multi;
-
- /**
- * CURL share handle
- */
- CURLSH *share;
-
- /**
- * Perform task handle
- */
- struct GNUNET_SCHEDULER_Task *perform_task;
-};
-
-/**
- * Type of requests we currently have
- */
-enum RequestType
-{
- /**
- * No request
- */
- REQUEST_TYPE_NONE,
-
- /**
- * Current request is to receive mint's keys
- */
- REQUEST_TYPE_KEYSGET,
-
- /**
- * Current request is to submit a deposit permission and get its status
- */
- REQUEST_TYPE_DEPOSIT
-};
-
-
-/**
- * Handle to the mint
- */
-struct TALER_MINT_Handle
-{
- /**
- * The context of this handle
- */
- struct TALER_MINT_Context *ctx;
-
- /**
- * The hostname of the mint
- */
- char *hostname;
-
- /**
- * The CURL handle
- */
- CURL *curl;
-
- /**
- * Error buffer for CURL
- */
- char emsg[CURL_ERROR_SIZE];
-
- /**
- * Download buffer
- */
- void *buf;
-
- /**
- * The currently active request
- */
- union {
- /**
- * Used to denote no request if set to NULL
- */
- void *none;
-
- /**
- * Denom keys get request if REQUEST_TYPE_KEYSGET
- */
- struct TALER_MINT_KeysGetHandle *keys_get;
-
- /**
- * Deposit request if REQUEST_TYPE_DEPOSIT
- */
- struct TALER_MINT_DepositHandle *deposit;
- } req;
-
- /**
- * The size of the download buffer
- */
- size_t buf_size;
-
- /**
- * Active request type
- */
- enum RequestType req_type;
-
- /**
- * The service port of the mint
- */
- uint16_t port;
-
- /**
- * Are we connected to the mint?
- */
- uint8_t connected;
-
-};
-
-
-/**
- * A handle to get the keys of a mint
- */
-struct TALER_MINT_KeysGetHandle
-{
- /**
- * The connection to mint this request handle will use
- */
- struct TALER_MINT_Handle *mint;
-
- /**
- * The url for this handle
- */
- char *url;
-
- TALER_MINT_KeysGetCallback cb;
-
- void *cb_cls;
-
- TALER_MINT_ContinuationCallback cont_cb;
-
- void *cont_cls;
-};
-
+static int TALER_MINT_curl_fail;
/**
* A handle to submit a deposit permission and get its status
@@ -214,27 +81,6 @@ struct TALER_MINT_DepositHandle
};
-/**
- * Parses the timestamp encoded as ASCII string as UNIX timstamp.
- *
- * @param abs successfully parsed timestamp will be returned thru this parameter
- * @param tstamp_enc the ASCII encoding of the timestamp
- * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
- */
-static int
-parse_timestamp (struct GNUNET_TIME_Absolute *abs,
- const char *tstamp_enc)
-{
- unsigned long tstamp;
-
- if (1 != sscanf (tstamp_enc, "%lu", &tstamp))
- return GNUNET_SYSERR;
- *abs = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_zero_ (),
- GNUNET_TIME_relative_multiply
- (GNUNET_TIME_UNIT_SECONDS, tstamp));
- return GNUNET_OK;
-}
-
#define EXITIF(cond) \
do { \
@@ -244,341 +90,6 @@ parse_timestamp (struct GNUNET_TIME_Absolute *abs,
static int
-parse_json_signkey (struct TALER_MINT_SigningPublicKey **_sign_key,
- json_t *sign_key_obj,
- struct TALER_MasterPublicKeyP *master_key)
-{
- json_t *valid_from_obj;
- json_t *valid_until_obj;
- json_t *valid_legal_obj;
- json_t *key_obj;
- json_t *sig_obj;
- const char *valid_from_enc;
- const char *valid_until_enc;
- const char *valid_legal_enc;
- const char *key_enc;
- const char *sig_enc;
- struct TALER_MINT_SigningPublicKey *sign_key;
- struct TALER_MintSigningKeyValidityPS sign_key_issue;
- struct GNUNET_CRYPTO_EddsaSignature sig;
- struct GNUNET_TIME_Absolute valid_from;
- struct GNUNET_TIME_Absolute valid_until;
- struct GNUNET_TIME_Absolute valid_legal;
-
- EXITIF (JSON_OBJECT != json_typeof (sign_key_obj));
- EXITIF (NULL == (valid_from_obj = json_object_get (sign_key_obj,
- "stamp_start")));
- EXITIF (NULL == (valid_until_obj = json_object_get (sign_key_obj,
- "stamp_expire")));
- EXITIF (NULL == (valid_legal_obj = json_object_get (sign_key_obj,
- "stamp_end")));
- EXITIF (NULL == (key_obj = json_object_get (sign_key_obj, "key")));
- EXITIF (NULL == (sig_obj = json_object_get (sign_key_obj, "master_sig")));
- EXITIF (NULL == (valid_from_enc = json_string_value (valid_from_obj)));
- EXITIF (NULL == (valid_until_enc = json_string_value (valid_until_obj)));
- EXITIF (NULL == (valid_legal_enc = json_string_value (valid_legal_obj)));
- EXITIF (NULL == (key_enc = json_string_value (key_obj)));
- EXITIF (NULL == (sig_enc = json_string_value (sig_obj)));
- EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from,
- valid_from_enc));
- EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_until,
- valid_until_enc));
- EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_legal,
- valid_legal_enc));
- EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */
- EXITIF (103 != strlen (sig_enc)); /* strlen(base32(char[64])) = 103 */
- EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103,
- &sig, sizeof (sig)));
- memset (&sign_key_issue,
- 0,
- sizeof (sign_key_issue));
- EXITIF (GNUNET_SYSERR ==
- GNUNET_CRYPTO_eddsa_public_key_from_string (key_enc,
- 52,
- &sign_key_issue.signkey_pub.eddsa_pub));
- sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY);
- sign_key_issue.purpose.size =
- htonl (sizeof (sign_key_issue)
- - offsetof (struct TALER_MintSigningKeyValidityPS, purpose));
- sign_key_issue.master_public_key = *master_key;
- sign_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
- sign_key_issue.expire = GNUNET_TIME_absolute_hton (valid_until);
- sign_key_issue.end = GNUNET_TIME_absolute_hton (valid_legal);
- EXITIF (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
- &sign_key_issue.purpose,
- &sig,
- &master_key->eddsa_pub));
- sign_key = GNUNET_new (struct TALER_MINT_SigningPublicKey);
- sign_key->valid_from = valid_from;
- sign_key->valid_until = valid_until;
- sign_key->key = sign_key_issue.signkey_pub;
- *_sign_key = sign_key;
- return GNUNET_OK;
-
- EXITIF_exit:
- return GNUNET_SYSERR;
-}
-
-
-static int
-parse_json_amount (json_t *amount_obj, struct TALER_Amount *amt)
-{
- json_t *obj;
- const char *currency_str;
- int value;
- int fraction;
-
- EXITIF (NULL == (obj = json_object_get (amount_obj, "currency")));
- EXITIF (NULL == (currency_str = json_string_value (obj)));
- EXITIF (NULL == (obj = json_object_get (amount_obj, "value")));
- EXITIF (JSON_INTEGER != json_typeof (obj));
- EXITIF (0 > (value = json_integer_value (obj)));
- EXITIF (NULL == (obj = json_object_get (amount_obj, "fraction")));
- EXITIF (JSON_INTEGER != json_typeof (obj));
- EXITIF (0 > (fraction = json_integer_value (obj)));
- (void) memset (amt->currency, 0, sizeof (amt->currency));
- (void) strncpy (amt->currency, currency_str, sizeof (amt->currency) - 1);
- amt->value = (uint32_t) value;
- amt->fraction = (uint32_t) fraction;
- return GNUNET_OK;
-
- EXITIF_exit:
- return GNUNET_SYSERR;
-}
-
-
-/* FIXME: avoid useless ** for _denom_key! */
-static int
-parse_json_denomkey (struct TALER_MINT_DenomPublicKey **_denom_key,
- json_t *denom_key_obj,
- struct TALER_MasterPublicKeyP *master_key)
-{
- json_t *obj;
- const char *sig_enc;
- const char *deposit_valid_until_enc;
- const char *withdraw_valid_until_enc;
- const char *valid_from_enc;
- const char *key_enc;
- char *buf;
- size_t buf_size;
- struct TALER_MINT_DenomPublicKey *denom_key;
- struct GNUNET_TIME_Absolute valid_from;
- struct GNUNET_TIME_Absolute withdraw_valid_until;
- struct GNUNET_TIME_Absolute deposit_valid_until;
- struct TALER_Amount value;
- struct TALER_Amount fee_withdraw;
- struct TALER_Amount fee_deposit;
- struct TALER_Amount fee_refresh;
- struct TALER_DenominationKeyValidityPS denom_key_issue;
- struct GNUNET_CRYPTO_rsa_PublicKey *pk;
- struct GNUNET_CRYPTO_EddsaSignature sig;
-
- EXITIF (JSON_OBJECT != json_typeof (denom_key_obj));
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "master_sig")));
- EXITIF (NULL == (sig_enc = json_string_value (obj)));
- EXITIF (103 != strlen (sig_enc));
- EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103,
- &sig, sizeof (sig)));
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_deposit")));
- EXITIF (NULL == (deposit_valid_until_enc = json_string_value (obj)));
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_withdraw")));
- EXITIF (NULL == (withdraw_valid_until_enc = json_string_value (obj)));
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_start")));
- EXITIF (NULL == (valid_from_enc = json_string_value (obj)));
-
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "denom_pub")));
- EXITIF (NULL == (key_enc = json_string_value (obj)));
-
- EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, valid_from_enc));
- EXITIF (GNUNET_SYSERR == parse_timestamp (&withdraw_valid_until,
- withdraw_valid_until_enc));
- EXITIF (GNUNET_SYSERR == parse_timestamp (&deposit_valid_until,
- deposit_valid_until_enc));
-
- memset (&denom_key_issue, 0, sizeof (denom_key_issue));
-
- buf_size = (strlen (key_enc) * 5) / 8;
- buf = GNUNET_malloc (buf_size);
-
- EXITIF (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (key_enc, strlen (key_enc),
- buf,
- buf_size));
- pk = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size);
- GNUNET_free (buf);
-
- EXITIF (NULL == pk);
- GNUNET_CRYPTO_rsa_public_key_hash (pk,
- &denom_key_issue.denom_hash);
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "value")));
- EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &value));
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_withdraw")));
- EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_withdraw));
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_deposit")));
- EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_deposit));
- EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_refresh")));
- EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_refresh));
- denom_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
- denom_key_issue.purpose.size = htonl
- (sizeof (struct TALER_DenominationKeyValidityPS) -
- offsetof (struct TALER_DenominationKeyValidityPS, purpose));
- denom_key_issue.master = *master_key;
- denom_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
- denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until);
- denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until);
- TALER_amount_hton (&denom_key_issue.value,
- &value);
- TALER_amount_hton (&denom_key_issue.fee_withdraw,
- &fee_withdraw);
- TALER_amount_hton (&denom_key_issue.fee_deposit,
- &fee_deposit);
- TALER_amount_hton (&denom_key_issue.fee_refresh,
- &fee_refresh);
- EXITIF (GNUNET_SYSERR ==
- GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
- &denom_key_issue.purpose,
- &sig,
- &master_key->eddsa_pub));
- denom_key = GNUNET_new (struct TALER_MINT_DenomPublicKey);
- denom_key->key.rsa_public_key = pk;
- denom_key->valid_from = valid_from;
- denom_key->withdraw_valid_until = withdraw_valid_until;
- denom_key->deposit_valid_until = deposit_valid_until;
- denom_key->value = value;
- denom_key->fee_withdraw = fee_withdraw;
- denom_key->fee_deposit = fee_deposit;
- denom_key->fee_refresh = fee_refresh;
- *_denom_key = denom_key;
- return GNUNET_OK;
-
- EXITIF_exit:
- return GNUNET_SYSERR;
-}
-
-
-static int
-parse_response_keys_get (const char *in, size_t size,
- struct TALER_MINT_SigningPublicKey ***_sign_keys,
- unsigned int *_n_sign_keys,
- struct TALER_MINT_DenomPublicKey ***_denom_keys,
- unsigned int *_n_denom_keys)
-{
- json_t *resp_obj;
- struct TALER_MINT_DenomPublicKey **denom_keys;
- struct TALER_MasterPublicKeyP master_key;
- struct GNUNET_TIME_Absolute list_issue_date;
- struct TALER_MINT_SigningPublicKey **sign_keys;
- unsigned int n_denom_keys;
- unsigned int n_sign_keys;
- json_error_t error;
- unsigned int index;
- int OK;
-
- denom_keys = NULL;
- n_denom_keys = 0;
- sign_keys = NULL;
- n_sign_keys = 0;
- OK = 0;
- resp_obj = json_loadb (in, size,
- JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
- &error);
- if (NULL == resp_obj)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
- "Unable to parse received data as JSON object\n");
- return GNUNET_SYSERR;
- }
-
- EXITIF (JSON_OBJECT != json_typeof (resp_obj));
- {
- /* parse the master public key */
- json_t *master_key_obj;
- const char *master_key_enc;
-
- EXITIF (NULL == (master_key_obj = json_object_get (resp_obj, "TMH_master_public_key")));
- EXITIF (NULL == (master_key_enc = json_string_value (master_key_obj)));
- EXITIF (52 != strlen (master_key_enc)); /* strlen(base32(char[32])) = 52 */
- EXITIF (GNUNET_OK !=
- GNUNET_CRYPTO_eddsa_public_key_from_string (master_key_enc,
- 52,
- &master_key.eddsa_pub));
- }
- {
- /* parse the issue date of the response */
- json_t *list_issue_date_obj;
- const char *tstamp_enc;
-
- EXITIF (NULL == (list_issue_date_obj =
- json_object_get(resp_obj, "list_issue_date")));
- EXITIF (NULL == (tstamp_enc = json_string_value (list_issue_date_obj)));
- EXITIF (GNUNET_SYSERR == parse_timestamp (&list_issue_date, tstamp_enc));
- }
- {
- /* parse the signing keys */
- json_t *sign_keys_array;
- json_t *sign_key_obj;
-
- EXITIF (NULL == (sign_keys_array =
- json_object_get (resp_obj, "signkeys")));
- EXITIF (JSON_ARRAY != json_typeof (sign_keys_array));
- EXITIF (0 == (n_sign_keys = json_array_size (sign_keys_array)));
- sign_keys = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey *)
- * (n_sign_keys + 1));
- index = 0;
- json_array_foreach (sign_keys_array, index, sign_key_obj) {
- EXITIF (GNUNET_SYSERR == parse_json_signkey (&sign_keys[index],
- sign_key_obj,
- &master_key));
- }
- }
- {
- /* parse the denomination keys */
- json_t *denom_keys_array;
- json_t *denom_key_obj;
-
- EXITIF (NULL == (denom_keys_array = json_object_get (resp_obj, "denoms")));
- EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
- EXITIF (0 == (n_denom_keys = json_array_size (denom_keys_array)));
- denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey *)
- * (n_denom_keys + 1));
- index = 0;
- json_array_foreach (denom_keys_array, index, denom_key_obj) {
- EXITIF (GNUNET_SYSERR == parse_json_denomkey (&denom_keys[index],
- denom_key_obj,
- &master_key));
- }
- }
- OK = 1;
-
- EXITIF_exit:
- json_decref (resp_obj);
- if (!OK)
- {
- if (NULL != sign_keys)
- {
- for (index=0; NULL != sign_keys[index]; index++)
- GNUNET_free_non_null (sign_keys[index]);
- GNUNET_free (sign_keys);
- }
- if (NULL != denom_keys)
- {
- for (index=0; NULL != denom_keys[index]; index++)
- GNUNET_free_non_null (denom_keys[index]);
- GNUNET_free (denom_keys);
- }
- return GNUNET_SYSERR;
- }
-
- *_sign_keys = sign_keys;
- *_n_sign_keys = n_sign_keys;
- *_denom_keys = denom_keys;
- *_n_denom_keys = n_denom_keys;
- return GNUNET_OK;
-}
-
-
-int
parse_deposit_response (void *buf, size_t size, int *r_status, json_t **r_obj)
{
json_t *obj;
@@ -612,417 +123,6 @@ parse_deposit_response (void *buf, size_t size, int *r_status, json_t **r_obj)
#undef EXITIF
-static void
-mint_connect (struct TALER_MINT_Handle *mint)
-{
- struct TALER_MINT_Context *ctx = mint->ctx;
-
- GNUNET_assert (0 == mint->connected);
- GNUNET_assert (CURLM_OK == curl_multi_add_handle (ctx->multi, mint->curl));
- mint->connected = GNUNET_YES;
-}
-
-static void
-mint_disconnect (struct TALER_MINT_Handle *mint)
-{
- struct TALER_MINT_Context *ctx = mint->ctx;
-
- GNUNET_assert (GNUNET_YES == mint->connected);
- GNUNET_break (CURLM_OK == curl_multi_remove_handle (ctx->multi,
- mint->curl));
- mint->connected = GNUNET_NO;
- GNUNET_free_non_null (mint->buf);
- mint->buf = NULL;
- mint->buf_size = 0;
- mint->req_type = REQUEST_TYPE_NONE;
- mint->req.none = NULL;
-}
-
-static void
-cleanup_keys_get (struct TALER_MINT_KeysGetHandle *gh)
-{
- GNUNET_free (gh->url);
- GNUNET_free (gh);
-}
-
-static void
-cleanup_deposit (struct TALER_MINT_DepositHandle *dh)
-{
- curl_slist_free_all (dh->headers);
- GNUNET_free_non_null (dh->json_enc);
- GNUNET_free (dh->url);
- GNUNET_free (dh);
-}
-
-static void
-request_failed (struct TALER_MINT_Handle *mint, long resp_code)
-{
- switch (mint->req_type)
- {
- case REQUEST_TYPE_NONE:
- GNUNET_assert (0);
- break;
- case REQUEST_TYPE_KEYSGET:
- {
- struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get;
- TALER_MINT_ContinuationCallback cont_cb;
- void *cont_cls;
- GNUNET_assert (NULL != gh);
- cont_cb = gh->cont_cb;
- cont_cls = gh->cont_cls;
- cleanup_keys_get (gh);
- mint_disconnect (mint);
- cont_cb (cont_cls, mint->emsg);
- }
- break;
- case REQUEST_TYPE_DEPOSIT:
- {
- struct TALER_MINT_DepositHandle *dh = mint->req.deposit;
- TALER_MINT_DepositResultCallback cb = dh->cb;
- void *cls = dh->cb_cls;
-
- GNUNET_assert (NULL != dh);
- cleanup_deposit (dh);
- mint_disconnect (mint);
- cb (cls, 0, NULL, mint->emsg);
- }
- break;
- }
-}
-
-static void
-request_succeeded (struct TALER_MINT_Handle *mint, long resp_code)
-{
- char *emsg;
-
- emsg = NULL;
- switch (mint->req_type)
- {
- case REQUEST_TYPE_NONE:
- GNUNET_assert (0);
- break;
- case REQUEST_TYPE_KEYSGET:
- {
- struct TALER_MINT_KeysGetHandle *gh = mint->req.keys_get;
- TALER_MINT_ContinuationCallback cont_cb;
- void *cont_cls;
- struct TALER_MINT_SigningPublicKey **sign_keys;
- struct TALER_MINT_DenomPublicKey **denom_keys;
- unsigned int n_sign_keys;
- unsigned int n_denom_keys;
-
- GNUNET_assert (NULL != gh);
- cont_cb = gh->cont_cb;
- cont_cls = gh->cont_cls;
- if (200 == resp_code)
- {
- /* parse JSON object from the mint->buf which is of size mint->buf_size */
- if (GNUNET_OK ==
- parse_response_keys_get (mint->buf, mint->buf_size,
- &sign_keys, &n_sign_keys,
- &denom_keys, &n_denom_keys))
- gh->cb (gh->cb_cls, sign_keys, denom_keys);
- else
- emsg = GNUNET_strdup ("Error parsing response");
- }
- else
- GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code);
- cleanup_keys_get (gh);
- mint_disconnect (mint);
- cont_cb (cont_cls, emsg);
- }
- break;
- case REQUEST_TYPE_DEPOSIT:
- {
- struct TALER_MINT_DepositHandle *dh = mint->req.deposit;
- TALER_MINT_DepositResultCallback cb;
- void *cls;
- int status;
- json_t *obj;
-
- GNUNET_assert (NULL != dh);
- obj = NULL;
- cb = dh->cb;
- cls = dh->cb_cls;
- status = 0;
- if (200 == resp_code)
- {
- /* parse JSON object from the mint->buf which is of size mint->buf_size */
- if (GNUNET_OK !=
- parse_deposit_response (mint->buf, mint->buf_size,
- &status, &obj))
- emsg = GNUNET_strdup ("Error parsing response");
- }
- else
- GNUNET_asprintf (&emsg, "Failed with response code: %ld", resp_code);
- cleanup_deposit (dh);
- mint_disconnect (mint);
- cb (cls, status, obj, emsg);
- }
- break;
- }
- GNUNET_free_non_null (emsg);
-}
-
-
-static void
-do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc);
-
-static void
-perform (struct TALER_MINT_Context *ctx)
-{
- fd_set fd_rs;
- fd_set fd_ws;
- struct GNUNET_NETWORK_FDSet rs;
- struct GNUNET_NETWORK_FDSet ws;
- CURLMsg *cmsg;
- struct TALER_MINT_Handle *mint;
- long timeout;
- long resp_code;
- static unsigned int n_old;
- int n_running;
- int n_completed;
- int max_fd;
-
- n_completed = 0;
- curl_multi_perform (ctx->multi, &n_running);
- GNUNET_assert (0 <= n_running);
- if ((0 == n_running) || (n_running < n_old))
- {
- /* some requests were completed -- handle them */
- while (NULL != (cmsg = curl_multi_info_read (ctx->multi, &n_completed)))
- {
- GNUNET_break (CURLMSG_DONE == cmsg->msg); /* curl only has CURLMSG_DONE */
- GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
- CURLINFO_PRIVATE,
- (char *) &mint));
- GNUNET_assert (CURLE_OK == curl_easy_getinfo (cmsg->easy_handle,
- CURLINFO_RESPONSE_CODE,
- &resp_code));
- GNUNET_assert (ctx == mint->ctx); /* did we get the correct one? */
- if (CURLE_OK == cmsg->data.result)
- request_succeeded (mint, resp_code);
- else
- request_failed (mint, resp_code);
- }
- }
- n_old = n_running;
- /* reschedule perform() */
- if (0 != n_old)
- {
- FD_ZERO (&fd_rs);
- FD_ZERO (&fd_ws);
- GNUNET_assert (CURLM_OK == curl_multi_fdset (ctx->multi,
- &fd_rs,
- &fd_ws,
- NULL,
- &max_fd));
- if (-1 == max_fd)
- {
- ctx->perform_task = GNUNET_SCHEDULER_add_delayed
- (GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, 100),
- &do_perform, ctx);
- return;
- }
- GNUNET_assert (CURLM_OK == curl_multi_timeout (ctx->multi, &timeout));
- if (-1 == timeout)
- {
- timeout = 1000 * 60 * 5;
- }
- GNUNET_NETWORK_fdset_zero (&rs);
- GNUNET_NETWORK_fdset_zero (&ws);
- GNUNET_NETWORK_fdset_copy_native (&rs, &fd_rs, max_fd + 1);
- GNUNET_NETWORK_fdset_copy_native (&ws, &fd_ws, max_fd + 1);
- ctx->perform_task = GNUNET_SCHEDULER_add_select
- (GNUNET_SCHEDULER_PRIORITY_KEEP,
- GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_MILLISECONDS, timeout),
- &rs, &ws,
- &do_perform, ctx);
- }
-}
-
-
-static void
-do_perform (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
-{
- struct TALER_MINT_Context *ctx = cls;
-
- GNUNET_assert (NULL != ctx->perform_task);
- ctx->perform_task = NULL;
- perform (ctx);
-}
-
-static void
-perform_now (struct TALER_MINT_Context *ctx)
-{
- if (NULL != ctx->perform_task)
- {
- GNUNET_SCHEDULER_cancel (ctx->perform_task);
- ctx->perform_task = NULL;
- }
- ctx->perform_task = GNUNET_SCHEDULER_add_now (&do_perform, ctx);
-}
-
-
-/* This function gets called by libcurl as soon as there is data received that */
-/* needs to be saved. The size of the data pointed to by ptr is size */
-/* multiplied with nmemb, it will not be zero terminated. Return the number */
-/* of bytes actually taken care of. If that amount differs from the amount passed */
-/* to your function, it'll signal an error to the library. This will abort the */
-/* transfer and return CURLE_WRITE_ERROR. */
-
-/* From 7.18.0, the function can return CURL_WRITEFUNC_PAUSE which then will */
-/* cause writing to this connection to become paused. See */
-/* curl_easy_pause(3) for further details. */
-
-/* This function may be called with zero bytes data if the transferred file is */
-/* empty. */
-
-/* Set this option to NULL to get the internal default function. The internal */
-/* default function will write the data to the FILE * given with */
-/* CURLOPT_WRITEDATA. */
-
-/* Set the userdata argument with the CURLOPT_WRITEDATA option. */
-
-/* The callback function will be passed as much data as possible in all invokes, */
-/* but you cannot possibly make any assumptions. It may be one byte, it may be */
-/* thousands. The maximum amount of body data that can be passed to the write */
-/* callback is defined in the curl.h header file: CURL_MAX_WRITE_SIZE (the usual */
-/* default is 16K). If you however have CURLOPT_HEADER set, which sends */
-/* header data to the write callback, you can get up to */
-/* CURL_MAX_HTTP_HEADER bytes of header data passed into it. This usually */
-/* means 100K. */
-static size_t
-download (char *bufptr, size_t size, size_t nitems, void *cls)
-{
- struct TALER_MINT_Handle *mint = cls;
- size_t msize;
- void *buf;
-
- if (0 == size * nitems)
- {
- /* file is empty */
- return 0;
- }
- msize = size * nitems;
- mint->buf = GNUNET_realloc (mint->buf, mint->buf_size + msize);
- buf = mint->buf + mint->buf_size;
- memcpy (buf, bufptr, msize);
- mint->buf_size += msize;
- return msize;
-}
-
-
-/**
- * Initialise a connection to the mint.
- *
- * @param ctx the context
- * @param hostname the hostname of the mint
- * @param port the point where the mint's HTTP service is running.
- * @param master_key the offline master public key of the mint.
- * This is used to verify the responses of the mint.
- * @return the mint handle; NULL upon error
- */
-struct TALER_MINT_Handle *
-TALER_MINT_connect (struct TALER_MINT_Context *ctx,
- const char *hostname,
- uint16_t port,
- const struct TALER_MasterPublicKeyP *master_key)
-{
- struct TALER_MINT_Handle *mint;
-
- mint = GNUNET_new (struct TALER_MINT_Handle);
- mint->ctx = ctx;
- mint->hostname = GNUNET_strdup (hostname);
- mint->port = (0 != port) ? port : 80;
- mint->curl = curl_easy_init ();
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (mint->curl, CURLOPT_SHARE, ctx->share));
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (mint->curl, CURLOPT_ERRORBUFFER, mint->emsg));
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (mint->curl, CURLOPT_WRITEFUNCTION, &download));
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (mint->curl, CURLOPT_WRITEDATA, mint));
- GNUNET_assert (CURLE_OK == curl_easy_setopt (mint->curl, CURLOPT_PRIVATE, mint));
- return mint;
-}
-
-
-/**
- * Disconnect from the mint
- *
- * @param mint the mint handle
- */
-void
-TALER_MINT_disconnect (struct TALER_MINT_Handle *mint)
-{
- if (GNUNET_YES == mint->connected)
- mint_disconnect (mint);
- curl_easy_cleanup (mint->curl);
- GNUNET_free (mint->hostname);
- GNUNET_free (mint);
-}
-
-/**
- * Get the signing and denomination key of the mint.
- *
- * @param mint handle to the mint
- * @param cb the callback to call with each retrieved denomination key
- * @param cb_cls closure for the above callback
- * @param cont_cb the callback to call after completing this asynchronous call
- * @param cont_cls the closure for the continuation callback
- * @return a handle to this asynchronous call; NULL upon eror
- */
-struct TALER_MINT_KeysGetHandle *
-TALER_MINT_keys_get (struct TALER_MINT_Handle *mint,
- TALER_MINT_KeysGetCallback cb,
- void *cb_cls,
- TALER_MINT_ContinuationCallback cont_cb,
- void *cont_cls)
-{
- struct TALER_MINT_KeysGetHandle *gh;
-
- GNUNET_assert (REQUEST_TYPE_NONE == mint->req_type);
- gh = GNUNET_new (struct TALER_MINT_KeysGetHandle);
- gh->mint = mint;
- mint->req_type = REQUEST_TYPE_KEYSGET;
- mint->req.keys_get = gh;
- gh->cb = cb;
- gh->cb_cls = cb_cls;
- gh->cont_cb = cont_cb;
- gh->cont_cls = cont_cls;
- GNUNET_asprintf (&gh->url,
- "http://%s:%hu/keys",
- mint->hostname,
- mint->port);
- GNUNET_assert (CURLE_OK ==
- curl_easy_setopt (mint->curl,
- CURLOPT_URL,
- gh->url));
- if (GNUNET_NO == mint->connected)
- mint_connect (mint);
- perform_now (mint->ctx);
- return gh;
-}
-
-
-/**
- * Cancel the asynchronous call initiated by TALER_MINT_keys_get(). This
- * should not be called if either of the @a TALER_MINT_KeysGetCallback or
- * @a TALER_MINT_ContinuationCallback passed to TALER_MINT_keys_get() have
- * been called.
- *
- * @param get the handle for retrieving the keys
- */
-void
-TALER_MINT_keys_get_cancel (struct TALER_MINT_KeysGetHandle *get)
-{
- struct TALER_MINT_Handle *mint = get->mint;
-
- mint_disconnect (mint);
- cleanup_keys_get (get);
-}
/**
* Submit a deposit permission to the mint and get the mint's response
@@ -1072,95 +172,4 @@ TALER_MINT_deposit_submit_json (struct TALER_MINT_Handle *mint,
}
-/**
- * Cancel a deposit permission request. This function cannot be used on a
- * request handle if a response is already served for it.
- *
- * @param deposit the deposit permission request handle
- */
-void
-TALER_MINT_deposit_submit_cancel (struct TALER_MINT_DepositHandle *deposit)
-{
- struct TALER_MINT_Handle *mint = deposit->mint;
-
- mint_disconnect (mint);
- cleanup_deposit (deposit);
-}
-
-
-/**
- * Initialise this library. This function should be called before using any of
- * the following functions.
- *
- * @return library context
- */
-struct TALER_MINT_Context *
-TALER_MINT_init ()
-{
- struct TALER_MINT_Context *ctx;
- CURLM *multi;
- CURLSH *share;
-
- if (fail)
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "cURL was not initialised properly\n");
- return NULL;
- }
- if (NULL == (multi = curl_multi_init ()))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL multi handle\n");
- return NULL;
- }
- if (NULL == (share = curl_share_init ()))
- {
- GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Cannot create a cURL share handle\n");
- return NULL;
- }
- ctx = GNUNET_new (struct TALER_MINT_Context);
- ctx->multi = multi;
- ctx->share = share;
- return ctx;
-}
-
-
-/**
- * Cleanup library initialisation resources. This function should be called
- * after using this library to cleanup the resources occupied during library's
- * initialisation.
- *
- * @param ctx the library context
- */
-void
-TALER_MINT_cleanup (struct TALER_MINT_Context *ctx)
-{
- curl_share_cleanup (ctx->share);
- curl_multi_cleanup (ctx->multi);
- if (NULL != ctx->perform_task)
- {
- GNUNET_break (0); /* investigate why this happens */
- GNUNET_SCHEDULER_cancel (ctx->perform_task);
- }
- GNUNET_free (ctx);
-}
-
-
-__attribute__ ((constructor))
-void
-TALER_MINT_constructor__ (void)
-{
- CURLcode ret;
- if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
- {
- CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR, "curl_global_init", ret);
- fail = 1;
- }
-}
-
-__attribute__ ((destructor))
-void
-TALER_MINT_destructor__ (void)
-{
- if (fail)
- return;
- curl_global_cleanup ();
-}
+/* end of mint_api.c */
diff --git a/src/mint-lib/mint_api_context.c b/src/mint-lib/mint_api_context.c
new file mode 100644
index 000000000..7f70d092a
--- /dev/null
+++ b/src/mint-lib/mint_api_context.c
@@ -0,0 +1,370 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mint-lib/mint_api_context.c
+ * @brief Implementation of the context part of the mint's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include "taler_mint_service.h"
+#include "mint_api_context.h"
+
+
+/**
+ * Log error related to CURL operations.
+ *
+ * @param type log level
+ * @param function which function failed to run
+ * @param code what was the curl error code
+ */
+#define CURL_STRERROR(type, function, code) \
+ GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \
+ function, __FILE__, __LINE__, curl_easy_strerror (code));
+
+
+/**
+ * Failsafe flag. Raised if our constructor fails to initialize
+ * the Curl library.
+ */
+static int TALER_MINT_curl_fail;
+
+
+/**
+ * Jobs are CURL requests running within a `struct TALER_MINT_Context`.
+ */
+struct MAC_Job
+{
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *next;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *prev;
+
+ /**
+ * Easy handle of the job.
+ */
+ CURL *easy_handle;
+
+ /**
+ * Context this job runs in.
+ */
+ struct TALER_MINT_Context *ctx;
+
+ /**
+ * Function to call upon completion.
+ */
+ MAC_JobCompletionCallback jcc;
+
+ /**
+ * Closure for @e jcc.
+ */
+ void *jcc_cls;
+
+};
+
+
+/**
+ * Context
+ */
+struct TALER_MINT_Context
+{
+ /**
+ * Curl multi handle
+ */
+ CURLM *multi;
+
+ /**
+ * Curl share handle
+ */
+ CURLSH *share;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *jobs_head;
+
+ /**
+ * We keep jobs in a DLL.
+ */
+ struct MAC_Job *jobs_tail;
+
+};
+
+
+/**
+ * Initialise this library. This function should be called before using any of
+ * the following functions.
+ *
+ * @return library context
+ */
+struct TALER_MINT_Context *
+TALER_MINT_init ()
+{
+ struct TALER_MINT_Context *ctx;
+ CURLM *multi;
+ CURLSH *share;
+
+ if (TALER_MINT_curl_fail)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Curl was not initialised properly\n");
+ return NULL;
+ }
+ if (NULL == (multi = curl_multi_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to create a Curl multi handle\n");
+ return NULL;
+ }
+ if (NULL == (share = curl_share_init ()))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to create a Curl share handle\n");
+ return NULL;
+ }
+ ctx = GNUNET_new (struct TALER_MINT_Context);
+ ctx->multi = multi;
+ ctx->share = share;
+ return ctx;
+}
+
+
+/**
+ * Schedule a CURL request to be executed and call the given @a jcc
+ * upon its completion. Note that the context will make use of the
+ * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
+ * instead use #MAC_easy_to_closure to extract the @a jcc_cls argument
+ * from a valid @a eh afterwards.
+ *
+ * @param ctx context to execute the job in
+ * @param eh curl easy handle for the request, will
+ * be executed AND cleaned up
+ * @param jcc callback to invoke upon completion
+ * @param jcc_cls closure for @a jcc
+ */
+struct MAC_Job *
+MAC_job_add (struct TALER_MINT_Context *ctx,
+ CURL *eh,
+ MAC_JobCompletionCallback jcc,
+ void *jcc_cls)
+{
+ struct MAC_Job *job;
+
+ job = GNUNET_new (struct MAC_Job);
+ job->easy_handle = eh;
+ job->ctx = ctx;
+ job->jcc = jcc;
+ job->jcc_cls = jcc_cls;
+ GNUNET_CONTAINER_DLL_insert (ctx->jobs_head,
+ ctx->jobs_tail,
+ job);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_PRIVATE,
+ job));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (eh,
+ CURLOPT_SHARE,
+ ctx->share));
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_add_handle (ctx->multi,
+ eh));
+ return job;
+}
+
+
+/**
+ * Obtain the `jcc_cls` argument from an `eh` that was
+ * given to #MAC_job_add().
+ *
+ * @param eh easy handle that was used
+ * @return the `jcc_cls` that was given to #MAC_job_add().
+ */
+void *
+MAC_easy_to_closure (CURL *eh)
+{
+ struct MAC_Job *job;
+
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_getinfo (eh,
+ CURLINFO_PRIVATE,
+ (char *) &job));
+ return job->jcc_cls;
+}
+
+
+/**
+ * Cancel a job. Must only be called before the job completion
+ * callback is called for the respective job.
+ *
+ * @param job job to cancel
+ */
+void
+MAC_job_cancel (struct MAC_Job *job)
+{
+ struct TALER_MINT_Context *ctx = job->ctx;
+
+ GNUNET_CONTAINER_DLL_remove (ctx->jobs_head,
+ ctx->jobs_tail,
+ job);
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_remove_handle (ctx->multi,
+ job->easy_handle));
+ curl_easy_cleanup (job->easy_handle);
+ GNUNET_free (job);
+}
+
+
+/**
+ * Run the main event loop for the Taler interaction.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MINT_perform (struct TALER_MINT_Context *ctx)
+{
+ CURLMsg *cmsg;
+ struct MAC_Job *job;
+ int n_running;
+ int n_completed;
+
+ (void) curl_multi_perform (ctx->multi,
+ &n_running);
+ while (NULL != (cmsg = curl_multi_info_read (ctx->multi,
+ &n_completed)))
+ {
+ /* Only documented return value is CURLMSG_DONE */
+ GNUNET_break (CURLMSG_DONE == cmsg->msg);
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_getinfo (cmsg->easy_handle,
+ CURLINFO_PRIVATE,
+ (char *) &job));
+ GNUNET_assert (job->ctx == ctx);
+ job->jcc (job->jcc_cls);
+ MAC_job_cancel (job);
+ }
+}
+
+
+/**
+ * Obtain the information for a select() call to wait until
+ * #TALER_MINT_perform() is ready again. Note that calling
+ * any other TALER_MINT-API may also imply that the library
+ * is again ready for #TALER_MINT_perform().
+ *
+ * Basically, a client should use this API to prepare for select(),
+ * then block on select(), then call #TALER_MINT_perform() and then
+ * start again until the work with the context is done.
+ *
+ * This function will NOT zero out the sets and assumes that @a max_fd
+ * and @a timeout are already set to minimal applicable values. It is
+ * safe to give this API FD-sets and @a max_fd and @a timeout that are
+ * already initialized to some other descriptors that need to go into
+ * the select() call.
+ *
+ * @param ctx context to get the event loop information for
+ * @param read_fd_set will be set for any pending read operations
+ * @param write_fd_set will be set for any pending write operations
+ * @param except_fd_set is here because curl_multi_fdset() has this argument
+ * @param max_fd set to the highest FD included in any set;
+ * if the existing sets have no FDs in it, the initial
+ * value should be "-1". (Note that `max_fd + 1` will need
+ * to be passed to select().)
+ * @param timeout set to the timeout in milliseconds (!); -1 means
+ * no timeout (NULL, blocking forever is OK), 0 means to
+ * proceed immediately with #TALER_MINT_perform().
+ */
+void
+TALER_MINT_get_select_info (struct TALER_MINT_Context *ctx,
+ fd_set *read_fd_set,
+ fd_set *write_fd_set,
+ fd_set *except_fd_set,
+ int *max_fd,
+ long *timeout)
+{
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_fdset (ctx->multi,
+ read_fd_set,
+ write_fd_set,
+ except_fd_set,
+ max_fd));
+ GNUNET_assert (CURLM_OK ==
+ curl_multi_timeout (ctx->multi,
+ timeout));
+ if ( (-1 == (*timeout)) &&
+ (NULL != ctx->jobs_head) )
+ *timeout = 1000 * 60 * 5; /* curl is not always good about giving timeouts */
+}
+
+
+/**
+ * Cleanup library initialisation resources. This function should be called
+ * after using this library to cleanup the resources occupied during library's
+ * initialisation.
+ *
+ * @param ctx the library context
+ */
+void
+TALER_MINT_fini (struct TALER_MINT_Context *ctx)
+{
+ /* all jobs must have been cancelled at this time, assert this */
+ GNUNET_assert (NULL == ctx->jobs_head);
+ curl_share_cleanup (ctx->share);
+ curl_multi_cleanup (ctx->multi);
+ GNUNET_free (ctx);
+}
+
+
+/**
+ * Initial global setup logic, specifically runs the Curl setup.
+ */
+__attribute__ ((constructor))
+void
+TALER_MINT_constructor__ (void)
+{
+ CURLcode ret;
+
+ if (CURLE_OK != (ret = curl_global_init (CURL_GLOBAL_DEFAULT)))
+ {
+ CURL_STRERROR (GNUNET_ERROR_TYPE_ERROR,
+ "curl_global_init",
+ ret);
+ TALER_MINT_curl_fail = 1;
+ }
+}
+
+
+/**
+ * Cleans up after us, specifically runs the Curl cleanup.
+ */
+__attribute__ ((destructor))
+void
+TALER_MINT_destructor__ (void)
+{
+ if (TALER_MINT_curl_fail)
+ return;
+ curl_global_cleanup ();
+}
+
+/* end of mint_api_context.c */
diff --git a/src/mint-lib/mint_api_context.h b/src/mint-lib/mint_api_context.h
new file mode 100644
index 000000000..b64f007bc
--- /dev/null
+++ b/src/mint-lib/mint_api_context.h
@@ -0,0 +1,83 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mint-lib/mint_api_context.h
+ * @brief Internal interface to the context part of the mint's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mint_service.h"
+#include "taler_signatures.h"
+
+
+/**
+ * Entry in the context's job queue.
+ */
+struct MAC_Job;
+
+/**
+ * Function to call upon completion of a job.
+ */
+typedef void
+(*MAC_JobCompletionCallback)(void *cls);
+
+
+/**
+ * Schedule a CURL request to be executed and call the given @a jcc
+ * upon its completion. Note that the context will make use of the
+ * CURLOPT_PRIVATE facility of the CURL @a eh. Applications can
+ * instead use #MAC_easy_to_closure to extract the @a jcc_cls argument
+ * from a valid @a eh afterwards.
+ *
+ * @param ctx context to execute the job in
+ * @param eh curl easy handle for the request, will
+ * be executed AND cleaned up
+ * @param jcc callback to invoke upon completion
+ * @param jcc_cls closure for @a jcc
+ */
+struct MAC_Job *
+MAC_job_add (struct TALER_MINT_Context *ctx,
+ CURL *eh,
+ MAC_JobCompletionCallback jcc,
+ void *jcc_cls);
+
+
+/**
+ * Obtain the `jcc_cls` argument from an `eh` that was
+ * given to #MAC_job_add().
+ *
+ * @param eh easy handle that was used
+ * @return the `jcc_cls` that was given to #MAC_job_add().
+ */
+void *
+MAC_easy_to_closure (CURL *eh);
+
+
+/**
+ * Cancel a job. Must only be called before the job completion
+ * callback is called for the respective job.
+ *
+ * @param job job to cancel
+ */
+void
+MAC_job_cancel (struct MAC_Job *job);
+
+
+/* end of mint_api_context.h */
diff --git a/src/mint-lib/mint_api_handle.c b/src/mint-lib/mint_api_handle.c
new file mode 100644
index 000000000..11c0b793a
--- /dev/null
+++ b/src/mint-lib/mint_api_handle.c
@@ -0,0 +1,821 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mint-lib/mint_api_handle.c
+ * @brief Implementation of the "handle" component of the mint's HTTP API
+ * @author Sree Harsha Totakura <sreeharsha@totakura.in>
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include <jansson.h>
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_mint_service.h"
+#include "taler_signatures.h"
+#include "mint_api_context.h"
+#include "mint_api_handle.h"
+
+
+/**
+ * Log error related to CURL operations.
+ *
+ * @param type log level
+ * @param function which function failed to run
+ * @param code what was the curl error code
+ */
+#define CURL_STRERROR(type, function, code) \
+ GNUNET_log (type, "Curl function `%s' has failed at `%s:%d' with error: %s", \
+ function, __FILE__, __LINE__, curl_easy_strerror (code));
+
+
+/**
+ * Print JSON parsing related error information
+ */
+#define JSON_WARN(error) \
+ GNUNET_log (GNUNET_ERROR_TYPE_WARNING, \
+ "JSON parsing failed at %s:%u: %s (%s)", \
+ __FILE__, __LINE__, error.text, error.source)
+
+/**
+ * Stages of initialization for the `struct TALER_MINT_Handle`
+ */
+enum MintHandleState
+{
+ /**
+ * Just allocated.
+ */
+ MHS_INIT = 0,
+
+ /**
+ * Obtained the mint's certification data and keys.
+ */
+ MHS_CERT = 1,
+
+ /**
+ * Failed to initialize (fatal).
+ */
+ MHS_FAILED = 2
+};
+
+
+/**
+ * Data for the request to get the /keys of a mint.
+ */
+struct KeysRequest;
+
+
+/**
+ * Handle to the mint
+ */
+struct TALER_MINT_Handle
+{
+ /**
+ * The context of this handle
+ */
+ struct TALER_MINT_Context *ctx;
+
+ /**
+ * The URL of the mint (i.e. "http://mint.taler.net/")
+ */
+ char *url;
+
+ /**
+ * Function to call with the mint's certification data,
+ * NULL if this has already been done.
+ */
+ TALER_MINT_CertificationCallback cert_cb;
+
+ /**
+ * Closure to pass to @e cert_cb.
+ */
+ void *cert_cb_cls;
+
+ /**
+ * Data for the request to get the /keys of a mint,
+ * NULL once we are past stage #MHS_INIT.
+ */
+ struct KeysRequest *kr;
+
+ /**
+ * Key data of the mint, only valid if
+ * @e handshake_complete is past stage #MHS_CERT.
+ */
+ struct TALER_MINT_Keys key_data;
+
+ /**
+ * Stage of the mint's initialization routines.
+ */
+ enum MintHandleState state;
+
+};
+
+
+/* ***************** Internal /keys fetching ************* */
+
+/**
+ * Data for the request to get the /keys of a mint.
+ */
+struct KeysRequest
+{
+ /**
+ * The connection to mint this request handle will use
+ */
+ struct TALER_MINT_Handle *mint;
+
+ /**
+ * The url for this handle
+ */
+ char *url;
+
+ /**
+ * Entry for this request with the `struct TALER_MINT_Context`.
+ */
+ struct MAC_Job *job;
+
+ /**
+ * Error buffer for Curl. Do we need this?
+ */
+ char emsg[CURL_ERROR_SIZE];
+
+ /**
+ * Download buffer
+ */
+ void *buf;
+
+ /**
+ * The size of the download buffer
+ */
+ size_t buf_size;
+
+ /**
+ * Error code (based on libc errno) if we failed to download
+ * (i.e. response too large).
+ */
+ int eno;
+
+};
+
+
+/**
+ * Callback used when downloading the reply to a /keys request.
+ * Just appends all of the data to the `buf` in the
+ * `struct KeysRequest` for further processing. The size of
+ * the download is limited to #GNUNET_MAX_MALLOC_CHECKED, if
+ * the download exceeds this size, we abort with an error.
+ *
+ * @param bufptr data downloaded via HTTP
+ * @param size size of an item in @a bufptr
+ * @param nitems number of items in @a bufptr
+ * @param cls the `struct KeysRequest`
+ * @return number of bytes processed from @a bufptr
+ */
+static size_t
+keys_download_cb (char *bufptr,
+ size_t size,
+ size_t nitems,
+ void *cls)
+{
+ struct KeysRequest *kr = cls;
+ size_t msize;
+ void *buf;
+
+ if (0 == size * nitems)
+ {
+ /* Nothing (left) to do */
+ return 0;
+ }
+ msize = size * nitems;
+ if ( (msize + kr->buf_size) >= GNUNET_MAX_MALLOC_CHECKED)
+ {
+ kr->eno = ENOMEM;
+ return 0; /* signals an error to curl */
+ }
+ kr->buf = GNUNET_realloc (kr->buf,
+ kr->buf_size + msize);
+ buf = kr->buf + kr->buf_size;
+ memcpy (buf, bufptr, msize);
+ kr->buf_size += msize;
+ return msize;
+}
+
+
+/**
+ * Release memory occupied by a keys request.
+ * Note that this does not cancel the request
+ * itself.
+ *
+ * @param kr request to free
+ */
+static void
+free_keys_request (struct KeysRequest *kr)
+{
+ GNUNET_free_non_null (kr->buf);
+ GNUNET_free (kr->url);
+ GNUNET_free (kr);
+}
+
+
+/**
+ * Parses the timestamp encoded as ASCII string as UNIX timstamp.
+ * FIXME: we might want to move this function into libtalerutil.
+ *
+ * @param[out] abs successfully parsed timestamp will be returned thru this parameter
+ * @param tstamp_enc the ASCII encoding of the timestamp
+ * @return #GNUNET_OK upon success; #GNUNET_SYSERR upon failure
+ */
+static int
+parse_timestamp (struct GNUNET_TIME_Absolute *abs,
+ const char *tstamp_enc)
+{
+ unsigned long tstamp;
+
+ if (1 != sscanf (tstamp_enc, "%lu", &tstamp))
+ return GNUNET_SYSERR;
+ *abs = GNUNET_TIME_absolute_add (GNUNET_TIME_absolute_get_zero_ (),
+ GNUNET_TIME_relative_multiply
+ (GNUNET_TIME_UNIT_SECONDS, tstamp));
+ return GNUNET_OK;
+}
+
+
+#define EXITIF(cond) \
+ do { \
+ if (cond) { GNUNET_break (0); goto EXITIF_exit; } \
+ } while (0)
+
+
+/**
+ * Parse a mint's signing key encoded in JSON.
+ *
+ * @param[out] sign_key where to return the result
+ * @param[in] sign_key_obj json to parse
+ * @param master_key master key to use to verify signature
+ * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
+ * invalid or the json malformed.
+ */
+static int
+parse_json_signkey (struct TALER_MINT_SigningPublicKey *sign_key,
+ json_t *sign_key_obj,
+ const struct TALER_MasterPublicKeyP *master_key)
+{
+ // TODO: try to simplify...
+ json_t *valid_from_obj;
+ json_t *valid_until_obj;
+ json_t *valid_legal_obj;
+ json_t *key_obj;
+ json_t *sig_obj;
+ const char *valid_from_enc;
+ const char *valid_until_enc;
+ const char *valid_legal_enc;
+ const char *key_enc;
+ const char *sig_enc;
+ struct TALER_MintSigningKeyValidityPS sign_key_issue;
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+ struct GNUNET_TIME_Absolute valid_from;
+ struct GNUNET_TIME_Absolute valid_until;
+ struct GNUNET_TIME_Absolute valid_legal;
+
+ EXITIF (JSON_OBJECT != json_typeof (sign_key_obj));
+ EXITIF (NULL == (valid_from_obj = json_object_get (sign_key_obj,
+ "stamp_start")));
+ EXITIF (NULL == (valid_until_obj = json_object_get (sign_key_obj,
+ "stamp_expire")));
+ EXITIF (NULL == (valid_legal_obj = json_object_get (sign_key_obj,
+ "stamp_end")));
+ EXITIF (NULL == (key_obj = json_object_get (sign_key_obj, "key")));
+ EXITIF (NULL == (sig_obj = json_object_get (sign_key_obj, "master_sig")));
+ EXITIF (NULL == (valid_from_enc = json_string_value (valid_from_obj)));
+ EXITIF (NULL == (valid_until_enc = json_string_value (valid_until_obj)));
+ EXITIF (NULL == (valid_legal_enc = json_string_value (valid_legal_obj)));
+ EXITIF (NULL == (key_enc = json_string_value (key_obj)));
+ EXITIF (NULL == (sig_enc = json_string_value (sig_obj)));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from,
+ valid_from_enc));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_until,
+ valid_until_enc));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_legal,
+ valid_legal_enc));
+ EXITIF (52 != strlen (key_enc)); /* strlen(base32(char[32])) = 52 */
+ EXITIF (103 != strlen (sig_enc)); /* strlen(base32(char[64])) = 103 */
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103,
+ &sig, sizeof (sig)));
+ memset (&sign_key_issue,
+ 0,
+ sizeof (sign_key_issue));
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_eddsa_public_key_from_string (key_enc,
+ 52,
+ &sign_key_issue.signkey_pub.eddsa_pub));
+ sign_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY);
+ sign_key_issue.purpose.size =
+ htonl (sizeof (sign_key_issue)
+ - offsetof (struct TALER_MintSigningKeyValidityPS, purpose));
+ sign_key_issue.master_public_key = *master_key;
+ sign_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
+ sign_key_issue.expire = GNUNET_TIME_absolute_hton (valid_until);
+ sign_key_issue.end = GNUNET_TIME_absolute_hton (valid_legal);
+ EXITIF (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_SIGNING_KEY_VALIDITY,
+ &sign_key_issue.purpose,
+ &sig,
+ &master_key->eddsa_pub));
+ sign_key->valid_from = valid_from;
+ sign_key->valid_until = valid_until;
+ sign_key->key = sign_key_issue.signkey_pub;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Parse an amount given in JSON encoding.
+ *
+ * @param[in] amount_obj the amount in json
+ * @param[out] where to return the parsed amount
+ * @return #GNUNET_OK if all is well, #GNUNET_SYSERR on parse errors
+ */
+static int
+parse_json_amount (json_t *amount_obj,
+ struct TALER_Amount *amt)
+{
+ // FIXME: check for correctness...
+ json_t *obj;
+ const char *currency_str;
+ int value; // FIXME: bad data type! (64 bit!)
+ int fraction;
+
+ EXITIF (NULL == (obj = json_object_get (amount_obj, "currency")));
+ EXITIF (NULL == (currency_str = json_string_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (amount_obj, "value")));
+ EXITIF (JSON_INTEGER != json_typeof (obj));
+ EXITIF (0 > (value = json_integer_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (amount_obj, "fraction")));
+ EXITIF (JSON_INTEGER != json_typeof (obj));
+ EXITIF (0 > (fraction = json_integer_value (obj)));
+ (void) memset (amt->currency, 0, sizeof (amt->currency));
+ (void) strncpy (amt->currency, currency_str, sizeof (amt->currency) - 1);
+ amt->value = (uint32_t) value;
+ amt->fraction = (uint32_t) fraction;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Parse a mint's denomination key encoded in JSON.
+ *
+ * @param[out] denom_key where to return the result
+ * @param[in] denom_key_obj json to parse
+ * @param master_key master key to use to verify signature
+ * @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
+ * invalid or the json malformed.
+ */
+static int
+parse_json_denomkey (struct TALER_MINT_DenomPublicKey *denom_key,
+ json_t *denom_key_obj,
+ struct TALER_MasterPublicKeyP *master_key)
+{
+ // FIXME: check logic, try to simplify
+ json_t *obj;
+ const char *sig_enc;
+ const char *deposit_valid_until_enc;
+ const char *withdraw_valid_until_enc;
+ const char *valid_from_enc;
+ const char *key_enc;
+ char *buf;
+ size_t buf_size;
+ struct GNUNET_TIME_Absolute valid_from;
+ struct GNUNET_TIME_Absolute withdraw_valid_until;
+ struct GNUNET_TIME_Absolute deposit_valid_until;
+ struct TALER_Amount value;
+ struct TALER_Amount fee_withdraw;
+ struct TALER_Amount fee_deposit;
+ struct TALER_Amount fee_refresh;
+ struct TALER_DenominationKeyValidityPS denom_key_issue;
+ struct GNUNET_CRYPTO_rsa_PublicKey *pk;
+ struct GNUNET_CRYPTO_EddsaSignature sig;
+
+ EXITIF (JSON_OBJECT != json_typeof (denom_key_obj));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "master_sig")));
+ EXITIF (NULL == (sig_enc = json_string_value (obj)));
+ EXITIF (103 != strlen (sig_enc));
+ EXITIF (GNUNET_OK != GNUNET_STRINGS_string_to_data (sig_enc, 103,
+ &sig, sizeof (sig)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_deposit")));
+ EXITIF (NULL == (deposit_valid_until_enc = json_string_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_expire_withdraw")));
+ EXITIF (NULL == (withdraw_valid_until_enc = json_string_value (obj)));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "stamp_start")));
+ EXITIF (NULL == (valid_from_enc = json_string_value (obj)));
+
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "denom_pub")));
+ EXITIF (NULL == (key_enc = json_string_value (obj)));
+
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&valid_from, valid_from_enc));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&withdraw_valid_until,
+ withdraw_valid_until_enc));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&deposit_valid_until,
+ deposit_valid_until_enc));
+
+ memset (&denom_key_issue, 0, sizeof (denom_key_issue));
+
+ buf_size = (strlen (key_enc) * 5) / 8;
+ buf = GNUNET_malloc (buf_size);
+
+ EXITIF (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (key_enc, strlen (key_enc),
+ buf,
+ buf_size));
+ pk = GNUNET_CRYPTO_rsa_public_key_decode (buf, buf_size);
+ GNUNET_free (buf);
+
+ EXITIF (NULL == pk);
+ GNUNET_CRYPTO_rsa_public_key_hash (pk,
+ &denom_key_issue.denom_hash);
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "value")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &value));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_withdraw")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_withdraw));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_deposit")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_deposit));
+ EXITIF (NULL == (obj = json_object_get (denom_key_obj, "fee_refresh")));
+ EXITIF (GNUNET_SYSERR == parse_json_amount (obj, &fee_refresh));
+ denom_key_issue.purpose.purpose = htonl (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY);
+ denom_key_issue.purpose.size = htonl
+ (sizeof (struct TALER_DenominationKeyValidityPS) -
+ offsetof (struct TALER_DenominationKeyValidityPS, purpose));
+ denom_key_issue.master = *master_key;
+ denom_key_issue.start = GNUNET_TIME_absolute_hton (valid_from);
+ denom_key_issue.expire_withdraw = GNUNET_TIME_absolute_hton (withdraw_valid_until);
+ denom_key_issue.expire_spend = GNUNET_TIME_absolute_hton (deposit_valid_until);
+ TALER_amount_hton (&denom_key_issue.value,
+ &value);
+ TALER_amount_hton (&denom_key_issue.fee_withdraw,
+ &fee_withdraw);
+ TALER_amount_hton (&denom_key_issue.fee_deposit,
+ &fee_deposit);
+ TALER_amount_hton (&denom_key_issue.fee_refresh,
+ &fee_refresh);
+ EXITIF (GNUNET_SYSERR ==
+ GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_DENOMINATION_KEY_VALIDITY,
+ &denom_key_issue.purpose,
+ &sig,
+ &master_key->eddsa_pub));
+ denom_key->key.rsa_public_key = pk;
+ denom_key->valid_from = valid_from;
+ denom_key->withdraw_valid_until = withdraw_valid_until;
+ denom_key->deposit_valid_until = deposit_valid_until;
+ denom_key->value = value;
+ denom_key->fee_withdraw = fee_withdraw;
+ denom_key->fee_deposit = fee_deposit;
+ denom_key->fee_refresh = fee_refresh;
+ return GNUNET_OK;
+
+ EXITIF_exit:
+ return GNUNET_SYSERR;
+}
+
+
+/**
+ * Decode the JSON in @a resp_obj from the /keys response and store the data
+ * in the @a key_data.
+ *
+ * @param[in] resp_obj JSON object to parse
+ * @param[out] key_data where to store the results we decoded
+ * @return #GNUNET_OK on success, #GNUNET_SYSERR on error (malformed JSON)
+ */
+static int
+decode_keys_json (json_t *resp_obj,
+ struct TALER_MINT_Keys *key_data)
+{
+ struct GNUNET_TIME_Absolute list_issue_date;
+
+ if (JSON_OBJECT != json_typeof (resp_obj))
+ return GNUNET_SYSERR;
+
+ /* parse the master public key */
+ {
+ json_t *master_key_obj;
+ const char *master_key_enc;
+
+ EXITIF (NULL == (master_key_obj =
+ json_object_get (resp_obj,
+ "master_public_key")));
+ EXITIF (NULL == (master_key_enc =
+ json_string_value (master_key_obj)));
+ EXITIF (GNUNET_OK !=
+ GNUNET_CRYPTO_eddsa_public_key_from_string (master_key_enc,
+ strlen (master_key_enc),
+ &key_data->master_pub.eddsa_pub));
+ }
+
+ /* parse the issue date of the response */
+ {
+ json_t *list_issue_date_obj;
+ const char *tstamp_enc;
+
+ EXITIF (NULL == (list_issue_date_obj =
+ json_object_get (resp_obj,
+ "list_issue_date")));
+ EXITIF (NULL == (tstamp_enc = json_string_value (list_issue_date_obj)));
+ EXITIF (GNUNET_SYSERR == parse_timestamp (&list_issue_date,
+ tstamp_enc));
+ }
+
+ /* parse the signing keys */
+ {
+ json_t *sign_keys_array;
+ json_t *sign_key_obj;
+ unsigned int index;
+
+ EXITIF (NULL == (sign_keys_array =
+ json_object_get (resp_obj,
+ "signkeys")));
+ EXITIF (JSON_ARRAY != json_typeof (sign_keys_array));
+ EXITIF (0 == (key_data->num_sign_keys =
+ json_array_size (sign_keys_array)));
+ key_data->sign_keys
+ = GNUNET_malloc (sizeof (struct TALER_MINT_SigningPublicKey)
+ * key_data->num_sign_keys);
+ index = 0;
+ json_array_foreach (sign_keys_array, index, sign_key_obj) {
+ EXITIF (GNUNET_SYSERR ==
+ parse_json_signkey (&key_data->sign_keys[index],
+ sign_key_obj,
+ &key_data->master_pub));
+ }
+ }
+
+ /* parse the denomination keys */
+ {
+ json_t *denom_keys_array;
+ json_t *denom_key_obj;
+ unsigned int index;
+
+ EXITIF (NULL == (denom_keys_array =
+ json_object_get (resp_obj, "denoms")));
+ EXITIF (JSON_ARRAY != json_typeof (denom_keys_array));
+ EXITIF (0 == (key_data->num_denom_keys = json_array_size (denom_keys_array)));
+ key_data->denom_keys = GNUNET_malloc (sizeof (struct TALER_MINT_DenomPublicKey)
+ * key_data->num_denom_keys);
+ index = 0;
+ json_array_foreach (denom_keys_array, index, denom_key_obj) {
+ EXITIF (GNUNET_SYSERR ==
+ parse_json_denomkey (&key_data->denom_keys[index],
+ denom_key_obj,
+ &key_data->master_pub));
+ }
+ }
+ return GNUNET_OK;
+
+ /* FIXME: parse the auditor keys */
+
+ /* FIXME: parse 'eddsa_sig' */
+
+ /* FIXME: validate signature... */
+
+ EXITIF_exit:
+ return GNUNET_OK;
+}
+
+
+/**
+ * We have successfully received the reply to the /keys
+ * request from the mint. We now need to parse the reply
+ * and, if successful, store the resulting information
+ * in the `key_data` structure.
+ *
+ * @param kr key request with all of the data to parse
+ * and references to the `struct TALER_MINT_Handle`
+ * where we need to store the result
+ * @return #GNUNET_OK on success,
+ * #GNUNET_SYSERR on failure
+ */
+static int
+parse_response_keys_get (struct KeysRequest *kr)
+{
+ json_t *resp_obj;
+ json_error_t error;
+ int ret;
+
+ resp_obj = json_loadb (kr->buf,
+ kr->buf_size,
+ JSON_REJECT_DUPLICATES | JSON_DISABLE_EOF_CHECK,
+ &error);
+ if (NULL == resp_obj)
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Unable to parse received /keys data as JSON object\n");
+ GNUNET_free_non_null (kr->buf);
+ kr->buf = NULL;
+ kr->buf_size = 0;
+ return GNUNET_SYSERR;
+ }
+ GNUNET_free_non_null (kr->buf);
+ kr->buf = NULL;
+ kr->buf_size = 0;
+ ret = decode_keys_json (resp_obj,
+ &kr->mint->key_data);
+ json_decref (resp_obj);
+ return ret;
+}
+
+
+/**
+ * Callback used when downloading the reply to a /keys request
+ * is complete.
+ *
+ * @param cls the `struct KeysRequest`
+ */
+static void
+keys_completed_cb (void *cls)
+{
+ struct KeysRequest *kr = cls;
+ struct TALER_MINT_Handle *mint = kr->mint;
+
+ if ( (0 != kr->eno) ||
+ (GNUNET_OK !=
+ parse_response_keys_get (kr)) )
+ {
+ mint->kr = NULL;
+ free_keys_request (kr);
+ mint->state = MHS_FAILED;
+ /* notify application that we failed */
+ if (NULL != mint->cert_cb)
+ {
+ mint->cert_cb (mint->cert_cb_cls,
+ NULL);
+ mint->cert_cb = NULL;
+ }
+ return;
+ }
+ mint->kr = NULL;
+ free_keys_request (kr);
+ mint->state = MHS_CERT;
+ /* notify application about the key information */
+ if (NULL != mint->cert_cb)
+ {
+ mint->cert_cb (mint->cert_cb_cls,
+ &mint->key_data);
+ mint->cert_cb = NULL;
+ }
+}
+
+
+/* ********************* library internal API ********* */
+
+
+/**
+ * Get the context of a mint.
+ *
+ * @param h the mint handle to query
+ * @return ctx context to execute jobs in
+ */
+struct TALER_MINT_Context *
+MAH_handle_to_context (struct TALER_MINT_Handle *h)
+{
+ return h->ctx;
+}
+
+
+/**
+ * Check if the handle is ready to process requests.
+ *
+ * @param h the mint handle to query
+ * @return #GNUNET_YES if we are ready, #GNUNET_NO if not
+ */
+int
+MAH_handle_is_ready (struct TALER_MINT_Handle *h)
+{
+ return (MHS_CERT == h->state) ? GNUNET_YES : GNUNET_NO;
+}
+
+
+/**
+ * Obtain the URL to use for an API request.
+ *
+ * @param h the mint handle to query
+ * @param path Taler API path (i.e. "/withdraw/sign")
+ * @return the full URI to use with cURL
+ */
+char *
+MAH_path_to_url (struct TALER_MINT_Handle *h,
+ const char *path)
+{
+ char *url;
+
+ GNUNET_asprintf (&url,
+ "%s%s",
+ h->url,
+ path);
+ return url;
+}
+
+
+/* ********************* public API ******************* */
+
+/**
+ * Initialise a connection to the mint. Will connect to the
+ * mint and obtain information about the mint's master public
+ * key and the mint's auditor. The respective information will
+ * be passed to the @a cert_cb once available, and all future
+ * interactions with the mint will be checked to be signed
+ * (where appropriate) by the respective master key.
+ *
+ * @param ctx the context
+ * @param url HTTP base URL for the mint
+ * @param cert_cb function to call with the mint's certification information
+ * @param cert_cb_cls closure for @a cert_cb
+ * @param ... list of additional arguments, terminated by #TALER_MINT_OPTION_END.
+ * @return the mint handle; NULL upon error
+ */
+struct TALER_MINT_Handle *
+TALER_MINT_connect (struct TALER_MINT_Context *ctx,
+ const char *url,
+ TALER_MINT_CertificationCallback cert_cb,
+ void *cert_cb_cls,
+ ...)
+{
+ struct TALER_MINT_Handle *mint;
+ struct KeysRequest *kr;
+ CURL *c;
+
+ mint = GNUNET_new (struct TALER_MINT_Handle);
+ mint->ctx = ctx;
+ mint->url = GNUNET_strdup (url);
+ kr = GNUNET_new (struct KeysRequest);
+ kr->mint = mint;
+ kr->url = MAH_path_to_url (mint, "/keys");
+ c = curl_easy_init ();
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (c,
+ CURLOPT_URL,
+ kr->url));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (c,
+ CURLOPT_ERRORBUFFER,
+ kr->emsg));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (c,
+ CURLOPT_WRITEFUNCTION,
+ &keys_download_cb));
+ GNUNET_assert (CURLE_OK ==
+ curl_easy_setopt (c,
+ CURLOPT_WRITEDATA,
+ kr));
+ kr->job = MAC_job_add (mint->ctx,
+ c,
+ &keys_completed_cb,
+ kr);
+ mint->kr = kr;
+ return mint;
+}
+
+
+/**
+ * Disconnect from the mint
+ *
+ * @param mint the mint handle
+ */
+void
+TALER_MINT_disconnect (struct TALER_MINT_Handle *mint)
+{
+ if (NULL != mint->kr)
+ {
+ MAC_job_cancel (mint->kr->job);
+ free_keys_request (mint->kr);
+ mint->kr = NULL;
+ }
+ GNUNET_array_grow (mint->key_data.sign_keys,
+ mint->key_data.num_sign_keys,
+ 0);
+ GNUNET_array_grow (mint->key_data.denom_keys,
+ mint->key_data.num_denom_keys,
+ 0);
+ GNUNET_array_grow (mint->key_data.auditors,
+ mint->key_data.num_auditors,
+ 0);
+ GNUNET_free (mint->url);
+ GNUNET_free (mint);
+}
+
+
+/* end of mint_api_handle.c */
diff --git a/src/mint-lib/mint_api_handle.h b/src/mint-lib/mint_api_handle.h
new file mode 100644
index 000000000..aeaeeb593
--- /dev/null
+++ b/src/mint-lib/mint_api_handle.h
@@ -0,0 +1,59 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2014, 2015 Christian Grothoff (and other contributing authors)
+
+ 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, If not, see
+ <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mint-lib/mint_api_handle.h
+ * @brief Internal interface to the handle part of the mint's HTTP API
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <curl/curl.h>
+#include "taler_mint_service.h"
+
+
+/**
+ * Get the context of a mint.
+ *
+ * @param h the mint handle to query
+ * @return ctx context to execute jobs in
+ */
+struct TALER_MINT_Context *
+MAH_handle_to_context (struct TALER_MINT_Handle *h);
+
+
+/**
+ * Check if the handle is ready to process requests.
+ *
+ * @param h the mint handle to query
+ * @return #GNUNET_YES if we are ready, #GNUNET_NO if not
+ */
+int
+MAH_handle_is_ready (struct TALER_MINT_Handle *h);
+
+
+/**
+ * Obtain the URL to use for an API request.
+ *
+ * @param h the mint handle to query
+ * @param path Taler API path (i.e. "/withdraw/sign")
+ * @return the full URI to use with cURL
+ */
+char *
+MAH_path_to_url (struct TALER_MINT_Handle *h,
+ const char *path);
+
+
+/* end of mint_api_handle.h */
diff --git a/src/mint-lib/test_mint_api.c b/src/mint-lib/test_mint_api.c
index a068dde93..0b429d889 100644
--- a/src/mint-lib/test_mint_api.c
+++ b/src/mint-lib/test_mint_api.c
@@ -49,7 +49,7 @@ do_shutdown (void *cls, const struct GNUNET_SCHEDULER_TaskContext *tc)
dh = NULL;
TALER_MINT_disconnect (mint);
mint = NULL;
- TALER_MINT_cleanup (ctx);
+ TALER_MINT_fini (ctx);
ctx = NULL;
}