summaryrefslogtreecommitdiff
path: root/src/mint-lib/mint_api_handle.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/mint-lib/mint_api_handle.c')
-rw-r--r--src/mint-lib/mint_api_handle.c821
1 files changed, 821 insertions, 0 deletions
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 */