skeleton for libtalerauditor
This commit is contained in:
parent
8eda33bbe8
commit
e83964badb
36
src/auditor-lib/Makefile.am
Normal file
36
src/auditor-lib/Makefile.am
Normal file
@ -0,0 +1,36 @@
|
||||
# This Makefile.am is in the public domain
|
||||
AM_CPPFLAGS = -I$(top_srcdir)/src/include
|
||||
|
||||
if USE_COVERAGE
|
||||
AM_CFLAGS = --coverage -O0
|
||||
XLIB = -lgcov
|
||||
endif
|
||||
|
||||
lib_LTLIBRARIES = \
|
||||
libtalerauditor
|
||||
|
||||
libtalerauditor_la_LDFLAGS = \
|
||||
-version-info 0:0:0 \
|
||||
-no-undefined
|
||||
libtalerauditor_la_SOURCES = \
|
||||
curl_defaults.c \
|
||||
auditor_api_common.c \
|
||||
auditor_api_handle.c auditor_api_handle.h \
|
||||
auditor_api_deposit_confirmation.c
|
||||
libtalerauditor_la_LIBADD = \
|
||||
$(top_builddir)/src/json/libtalerjson.la \
|
||||
$(top_builddir)/src/util/libtalerutil.la \
|
||||
-lgnunetcurl \
|
||||
-lgnunetjson \
|
||||
-lgnunetutil \
|
||||
-ljansson \
|
||||
$(XLIB)
|
||||
|
||||
if HAVE_LIBCURL
|
||||
libtalerauditor_la_LIBADD += -lcurl
|
||||
else
|
||||
if HAVE_LIBGNURL
|
||||
libtalerauditor_la_LIBADD += -lgnurl
|
||||
endif
|
||||
endif
|
||||
|
328
src/auditor-lib/auditor_api_common.c
Normal file
328
src/auditor-lib/auditor_api_common.c
Normal file
@ -0,0 +1,328 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2015-2017 Taler Systems SA
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* @file exchange-lib/exchange_api_common.c
|
||||
* @brief common functions for the exchange API
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
#include "platform.h"
|
||||
#include "taler_json_lib.h"
|
||||
#include <gnunet/gnunet_curl_lib.h>
|
||||
#include "exchange_api_handle.h"
|
||||
#include "taler_signatures.h"
|
||||
|
||||
|
||||
/**
|
||||
* Verify a coins transaction history as returned by the exchange.
|
||||
*
|
||||
* @param currency expected currency for the coin
|
||||
* @param coin_pub public key of the coin
|
||||
* @param history history of the coin in json encoding
|
||||
* @param[out] total how much of the coin has been spent according to @a history
|
||||
* @return #GNUNET_OK if @a history is valid, #GNUNET_SYSERR if not
|
||||
*/
|
||||
int
|
||||
TALER_EXCHANGE_verify_coin_history (const char *currency,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
json_t *history,
|
||||
struct TALER_Amount *total)
|
||||
{
|
||||
size_t len;
|
||||
int add;
|
||||
struct TALER_Amount rtotal;
|
||||
|
||||
if (NULL == history)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
len = json_array_size (history);
|
||||
if (0 == len)
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_get_zero (currency,
|
||||
total));
|
||||
GNUNET_assert (GNUNET_OK ==
|
||||
TALER_amount_get_zero (currency,
|
||||
&rtotal));
|
||||
for (size_t off=0;off<len;off++)
|
||||
{
|
||||
json_t *transaction;
|
||||
struct TALER_Amount amount;
|
||||
const char *type;
|
||||
struct GNUNET_JSON_Specification spec_glob[] = {
|
||||
TALER_JSON_spec_amount ("amount",
|
||||
&amount),
|
||||
GNUNET_JSON_spec_string ("type",
|
||||
&type),
|
||||
GNUNET_JSON_spec_end()
|
||||
};
|
||||
|
||||
transaction = json_array_get (history,
|
||||
off);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (transaction,
|
||||
spec_glob,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
add = GNUNET_SYSERR;
|
||||
if (0 == strcasecmp (type,
|
||||
"DEPOSIT"))
|
||||
{
|
||||
struct TALER_DepositRequestPS dr;
|
||||
struct TALER_CoinSpendSignatureP sig;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("coin_sig",
|
||||
&sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
|
||||
&dr.h_contract_terms),
|
||||
GNUNET_JSON_spec_fixed_auto ("h_wire",
|
||||
&dr.h_wire),
|
||||
GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
|
||||
&dr.timestamp),
|
||||
GNUNET_JSON_spec_absolute_time_nbo ("refund_deadline",
|
||||
&dr.refund_deadline),
|
||||
TALER_JSON_spec_amount_nbo ("deposit_fee",
|
||||
&dr.deposit_fee),
|
||||
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
|
||||
&dr.merchant),
|
||||
GNUNET_JSON_spec_end()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (transaction,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
dr.purpose.size = htonl (sizeof (dr));
|
||||
dr.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_DEPOSIT);
|
||||
TALER_amount_hton (&dr.amount_with_fee,
|
||||
&amount);
|
||||
dr.coin_pub = *coin_pub;
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT,
|
||||
&dr.purpose,
|
||||
&sig.eddsa_signature,
|
||||
&coin_pub->eddsa_pub))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* TODO: check that deposit fee and coin value match
|
||||
our expectations from /keys! */
|
||||
add = GNUNET_YES;
|
||||
}
|
||||
else if (0 == strcasecmp (type,
|
||||
"MELT"))
|
||||
{
|
||||
struct TALER_RefreshMeltCoinAffirmationPS rm;
|
||||
struct TALER_CoinSpendSignatureP sig;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("coin_sig",
|
||||
&sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("rc",
|
||||
&rm.rc),
|
||||
TALER_JSON_spec_amount_nbo ("melt_fee",
|
||||
&rm.melt_fee),
|
||||
GNUNET_JSON_spec_end()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (transaction,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
rm.purpose.size = htonl (sizeof (rm));
|
||||
rm.purpose.purpose = htonl (TALER_SIGNATURE_WALLET_COIN_MELT);
|
||||
TALER_amount_hton (&rm.amount_with_fee,
|
||||
&amount);
|
||||
rm.coin_pub = *coin_pub;
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_MELT,
|
||||
&rm.purpose,
|
||||
&sig.eddsa_signature,
|
||||
&coin_pub->eddsa_pub))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* TODO: check that deposit fee and coin value match
|
||||
our expectations from /keys! */
|
||||
add = GNUNET_YES;
|
||||
}
|
||||
else if (0 == strcasecmp (type,
|
||||
"REFUND"))
|
||||
{
|
||||
struct TALER_RefundRequestPS rr;
|
||||
struct TALER_MerchantSignatureP sig;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("merchant_sig",
|
||||
&sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
|
||||
&rr.h_contract_terms),
|
||||
GNUNET_JSON_spec_fixed_auto ("merchant_pub",
|
||||
&rr.merchant),
|
||||
GNUNET_JSON_spec_uint64 ("rtransaction_id",
|
||||
&rr.rtransaction_id),
|
||||
TALER_JSON_spec_amount_nbo ("refund_fee",
|
||||
&rr.refund_fee),
|
||||
GNUNET_JSON_spec_end()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (transaction,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
rr.purpose.size = htonl (sizeof (rr));
|
||||
rr.purpose.purpose = htonl (TALER_SIGNATURE_MERCHANT_REFUND);
|
||||
rr.coin_pub = *coin_pub;
|
||||
TALER_amount_hton (&rr.refund_amount,
|
||||
&amount);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MERCHANT_REFUND,
|
||||
&rr.purpose,
|
||||
&sig.eddsa_sig,
|
||||
&rr.merchant.eddsa_pub))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* NOTE: theoretically, we could also check that the given
|
||||
merchant_pub and h_contract_terms appear in the
|
||||
history under deposits. However, there is really no benefit
|
||||
for the exchange to lie here, so not checking is probably OK
|
||||
(an auditor ought to check, though). Then again, we similarly
|
||||
had no reason to check the merchant's signature (other than a
|
||||
well-formendess check). */
|
||||
/* TODO: check that deposit fee and coin value match
|
||||
our expectations from /keys! */
|
||||
add = GNUNET_NO;
|
||||
}
|
||||
else if (0 == strcasecmp (type,
|
||||
"PAYBACK"))
|
||||
{
|
||||
struct TALER_PaybackConfirmationPS pc;
|
||||
struct TALER_ExchangePublicKeyP exchange_pub;
|
||||
struct TALER_ExchangeSignatureP exchange_sig;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("exchange_sig",
|
||||
&exchange_sig),
|
||||
GNUNET_JSON_spec_fixed_auto ("exchange_pub",
|
||||
&exchange_pub),
|
||||
GNUNET_JSON_spec_fixed_auto ("reserve_pub",
|
||||
&pc.reserve_pub),
|
||||
GNUNET_JSON_spec_absolute_time_nbo ("timestamp",
|
||||
&pc.timestamp),
|
||||
GNUNET_JSON_spec_end()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (transaction,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
pc.purpose.size = htonl (sizeof (pc));
|
||||
pc.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK);
|
||||
pc.coin_pub = *coin_pub;
|
||||
TALER_amount_hton (&pc.payback_amount,
|
||||
&amount);
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_EXCHANGE_CONFIRM_PAYBACK,
|
||||
&pc.purpose,
|
||||
&exchange_sig.eddsa_signature,
|
||||
&exchange_pub.eddsa_pub))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
add = GNUNET_YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* signature not supported, new version on server? */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (GNUNET_YES == add)
|
||||
{
|
||||
/* This amount should be added to the total */
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (total,
|
||||
total,
|
||||
&amount))
|
||||
{
|
||||
/* overflow in history already!? inconceivable! Bad exchange! */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
/* This amount should be subtracted from the total.
|
||||
|
||||
However, for the implementation, we first *add* up all of
|
||||
these negative amounts, as we might get refunds before
|
||||
deposits from a semi-evil exchange. Then, at the end, we do
|
||||
the subtraction by calculating "total = total - rtotal" */
|
||||
GNUNET_assert (GNUNET_NO == add);
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_add (&rtotal,
|
||||
&rtotal,
|
||||
&amount))
|
||||
{
|
||||
/* overflow in refund history? inconceivable! Bad exchange! */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally, subtract 'rtotal' from total to handle the subtractions */
|
||||
if (GNUNET_OK !=
|
||||
TALER_amount_subtract (total,
|
||||
total,
|
||||
&rtotal))
|
||||
{
|
||||
/* underflow in history? inconceivable! Bad exchange! */
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/* end of exchange_api_common.c */
|
365
src/auditor-lib/auditor_api_deposit_confirmation.c
Normal file
365
src/auditor-lib/auditor_api_deposit_confirmation.c
Normal file
@ -0,0 +1,365 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2018 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* @file auditor-lib/auditor_api_deposit.c
|
||||
* @brief Implementation of the /deposit request of the auditor's HTTP API
|
||||
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
#include "platform.h"
|
||||
#include <jansson.h>
|
||||
#include <microhttpd.h> /* just for HTTP status codes */
|
||||
#include <gnunet/gnunet_util_lib.h>
|
||||
#include <gnunet/gnunet_json_lib.h>
|
||||
#include <gnunet/gnunet_curl_lib.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_auditor_service.h"
|
||||
#include "auditor_api_handle.h"
|
||||
#include "taler_signatures.h"
|
||||
#include "curl_defaults.h"
|
||||
|
||||
|
||||
/**
|
||||
* @brief A DepositConfirmation Handle
|
||||
*/
|
||||
struct TALER_AUDITOR_DepositConfirmationHandle
|
||||
{
|
||||
|
||||
/**
|
||||
* The connection to auditor this request handle will use
|
||||
*/
|
||||
struct TALER_AUDITOR_Handle *auditor;
|
||||
|
||||
/**
|
||||
* The url for this request.
|
||||
*/
|
||||
char *url;
|
||||
|
||||
/**
|
||||
* JSON encoding of the request to POST.
|
||||
*/
|
||||
char *json_enc;
|
||||
|
||||
/**
|
||||
* Handle for the request.
|
||||
*/
|
||||
struct GNUNET_CURL_Job *job;
|
||||
|
||||
/**
|
||||
* Function to call with the result.
|
||||
*/
|
||||
TALER_AUDITOR_DepositConfirmationResultCallback cb;
|
||||
|
||||
/**
|
||||
* Closure for @a cb.
|
||||
*/
|
||||
void *cb_cls;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Function called when we're done processing the
|
||||
* HTTP /deposit-confirmation request.
|
||||
*
|
||||
* @param cls the `struct TALER_AUDITOR_DepositConfirmationHandle`
|
||||
* @param response_code HTTP response code, 0 on error
|
||||
* @param json parsed JSON result, NULL on error
|
||||
*/
|
||||
static void
|
||||
handle_deposit_confirmation_finished (void *cls,
|
||||
long response_code,
|
||||
const json_t *json)
|
||||
{
|
||||
struct TALER_AUDITOR_DepositConfirmationHandle *dh = cls;
|
||||
struct TALER_AuditorPublicKeyP auditor_pub;
|
||||
struct TALER_AuditorPublicKeyP *ep = NULL;
|
||||
|
||||
dh->job = NULL;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
break;
|
||||
case MHD_HTTP_OK:
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
break;
|
||||
case MHD_HTTP_BAD_REQUEST:
|
||||
/* This should never happen, either us or the auditor is buggy
|
||||
(or API version conflict); just pass JSON reply to the application */
|
||||
break;
|
||||
case MHD_HTTP_UNAUTHORIZED:
|
||||
/* Nothing really to verify, auditor says one of the signatures is
|
||||
invalid; as we checked them, this should never happen, we
|
||||
should pass the JSON reply to the application */
|
||||
break;
|
||||
case MHD_HTTP_NOT_FOUND:
|
||||
/* Nothing really to verify, this should never
|
||||
happen, we should pass the JSON reply to the application */
|
||||
break;
|
||||
case MHD_HTTP_INTERNAL_SERVER_ERROR:
|
||||
/* Server had an internal issue; we should retry, but this API
|
||||
leaves this to the application */
|
||||
break;
|
||||
default:
|
||||
/* unexpected response code */
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u\n",
|
||||
(unsigned int) response_code);
|
||||
GNUNET_break (0);
|
||||
response_code = 0;
|
||||
break;
|
||||
}
|
||||
dh->cb (dh->cb_cls,
|
||||
response_code,
|
||||
TALER_JSON_get_error_code (json),
|
||||
json);
|
||||
TALER_AUDITOR_deposit_confirmation_cancel (dh);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Verify signature information about the deposit-confirmation.
|
||||
*
|
||||
* @param dki public key information
|
||||
* @param amount the amount to be deposit-confirmationed
|
||||
* @param h_wire hash of the merchant’s account details
|
||||
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
|
||||
* @param coin_pub coin’s public key
|
||||
* @param timestamp timestamp when the deposit-confirmation was finalized
|
||||
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
|
||||
* @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed)
|
||||
* @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT_CONFIRMATION made by the customer with the coin’s private key.
|
||||
* @return #GNUNET_OK if signatures are OK, #GNUNET_SYSERR if not
|
||||
*/
|
||||
static int
|
||||
verify_signatures (const struct TALER_Amount *amount,
|
||||
const struct GNUNET_HashCode *h_wire,
|
||||
const struct GNUNET_HashCode *h_contract_terms,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
struct GNUNET_TIME_Absolute timestamp,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
struct GNUNET_TIME_Absolute refund_deadline,
|
||||
const struct TALER_CoinSpendSignatureP *coin_sig)
|
||||
{
|
||||
struct TALER_DepositConfirmationRequestPS dr;
|
||||
struct TALER_CoinPublicInfo coin_info;
|
||||
|
||||
dr.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_DEPOSIT_CONFIRMATION);
|
||||
dr.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationRequestPS));
|
||||
dr.h_contract_terms = *h_contract_terms;
|
||||
dr.h_wire = *h_wire;
|
||||
dr.timestamp = GNUNET_TIME_absolute_hton (timestamp);
|
||||
dr.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
|
||||
TALER_amount_hton (&dr.amount_with_fee,
|
||||
amount);
|
||||
TALER_amount_hton (&dr.deposit_confirmation_fee,
|
||||
&dki->fee_deposit_confirmation);
|
||||
dr.merchant = *merchant_pub;
|
||||
dr.coin_pub = *coin_pub;
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_WALLET_COIN_DEPOSIT_CONFIRMATION,
|
||||
&dr.purpose,
|
||||
&coin_sig->eddsa_signature,
|
||||
&coin_pub->eddsa_pub))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
TALER_LOG_WARNING ("Invalid coin signature on /deposit-confirmation request!\n");
|
||||
{
|
||||
TALER_LOG_DEBUG ("... amount_with_fee was %s\n",
|
||||
TALER_amount2s (amount));
|
||||
TALER_LOG_DEBUG ("... deposit-confirmation_fee was %s\n",
|
||||
TALER_amount2s (&dki->fee_deposit_confirmation));
|
||||
}
|
||||
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
|
||||
/* check coin signature */
|
||||
coin_info.coin_pub = *coin_pub;
|
||||
coin_info.denom_pub = *denom_pub;
|
||||
coin_info.denom_sig = *denom_sig;
|
||||
if (GNUNET_YES !=
|
||||
TALER_test_coin_valid (&coin_info))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
TALER_LOG_WARNING ("Invalid coin passed for /deposit-confirmation\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (0 < TALER_amount_cmp (&dki->fee_deposit_confirmation,
|
||||
amount))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
TALER_LOG_WARNING ("DepositConfirmation amount smaller than fee\n");
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Submit a deposit-confirmation permission to the auditor and get the
|
||||
* auditor's response. Note that while we return the response
|
||||
* verbatim to the caller for further processing, we do already verify
|
||||
* that the response is well-formed. If the auditor's reply is not
|
||||
* well-formed, we return an HTTP status code of zero to @a cb.
|
||||
*
|
||||
* We also verify that the @a exchange_sig is valid for this deposit-confirmation
|
||||
* request, and that the @a master_sig is a valid signature for @a
|
||||
* exchange_pub. Also, the @a auditor must be ready to operate (i.e. have
|
||||
* finished processing the /version reply). If either check fails, we do
|
||||
* NOT initiate the transaction with the auditor and instead return NULL.
|
||||
*
|
||||
* @param auditor the auditor handle; the auditor must be ready to operate
|
||||
* @param amount the amount to be deposit-confirmationed
|
||||
* @param h_contract_terms hash of the contact of the merchant with the customer (further details are never disclosed to the auditor)
|
||||
* @param coin_pub coin’s public key
|
||||
* @param timestamp timestamp when the contract was finalized, must not be too far in the future
|
||||
* @param merchant_pub the public key of the merchant (used to identify the merchant for refund requests)
|
||||
* @param refund_deadline date until which the merchant can issue a refund to the customer via the auditor (can be zero if refunds are not allowed); must not be after the @a wire_deadline
|
||||
* @param coin_sig the signature made with purpose #TALER_SIGNATURE_WALLET_COIN_DEPOSIT-CONFIRMATION made by the customer with the coin’s private key.
|
||||
* @param cb the callback to call when a reply for this request is available
|
||||
* @param cb_cls closure for the above callback
|
||||
* @return a handle for this request; NULL if the inputs are invalid (i.e.
|
||||
* signatures fail to verify). In this case, the callback is not called.
|
||||
*/
|
||||
struct TALER_AUDITOR_DepositConfirmationHandle *
|
||||
TALER_AUDITOR_deposit_confirmation (struct TALER_AUDITOR_Handle *auditor,
|
||||
const struct GNUNET_HashCode *h_wire,
|
||||
const struct TALER_Amount *amount_without_fees,
|
||||
const struct GNUNET_HashCode *h_contract_terms,
|
||||
const struct TALER_CoinSpendPublicKeyP *coin_pub,
|
||||
struct GNUNET_TIME_Absolute timestamp,
|
||||
const struct TALER_MerchantPublicKeyP *merchant_pub,
|
||||
struct GNUNET_TIME_Absolute refund_deadline,
|
||||
TALER_AUDITOR_DepositConfirmationResultCallback cb,
|
||||
void *cb_cls)
|
||||
{
|
||||
struct TALER_AUDITOR_DepositConfirmationHandle *dh;
|
||||
struct GNUNET_CURL_Context *ctx;
|
||||
json_t *deposit_confirmation_obj;
|
||||
CURL *eh;
|
||||
struct TALER_Amount amount_without_fee;
|
||||
|
||||
(void) GNUNET_TIME_round_abs (&wire_deadline);
|
||||
(void) GNUNET_TIME_round_abs (&refund_deadline);
|
||||
GNUNET_assert (refund_deadline.abs_value_us <= wire_deadline.abs_value_us);
|
||||
GNUNET_assert (GNUNET_YES ==
|
||||
MAH_handle_is_ready (auditor));
|
||||
if (GNUNET_OK !=
|
||||
verify_signatures (amount,
|
||||
&h_wire,
|
||||
h_contract_terms,
|
||||
coin_pub,
|
||||
timestamp,
|
||||
merchant_pub,
|
||||
refund_deadline,
|
||||
coin_sig))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
deposit_confirmation_obj
|
||||
= json_pack ("{s:o, s:o," /* f/wire */
|
||||
" s:o, s:o," /* H_wire, h_contract_terms */
|
||||
" s:o, s:o," /* coin_pub, denom_pub */
|
||||
" s:o, s:o," /* ub_sig, timestamp */
|
||||
" s:o," /* merchant_pub */
|
||||
" s:o, s:o," /* refund_deadline, wire_deadline */
|
||||
" s:o}", /* coin_sig */
|
||||
"contribution", TALER_JSON_from_amount (amount),
|
||||
"H_wire", GNUNET_JSON_from_data_auto (&h_wire),
|
||||
"h_contract_terms", GNUNET_JSON_from_data_auto (h_contract_terms),
|
||||
"coin_pub", GNUNET_JSON_from_data_auto (coin_pub),
|
||||
"timestamp", GNUNET_JSON_from_time_abs (timestamp),
|
||||
"merchant_pub", GNUNET_JSON_from_data_auto (merchant_pub),
|
||||
"refund_deadline", GNUNET_JSON_from_time_abs (refund_deadline),
|
||||
"wire_transfer_deadline", GNUNET_JSON_from_time_abs (wire_deadline),
|
||||
"coin_sig", GNUNET_JSON_from_data_auto (coin_sig)
|
||||
);
|
||||
if (NULL == deposit_confirmation_obj)
|
||||
{
|
||||
GNUNET_break (0);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dh = GNUNET_new (struct TALER_AUDITOR_DepositConfirmationHandle);
|
||||
dh->auditor = auditor;
|
||||
dh->cb = cb;
|
||||
dh->cb_cls = cb_cls;
|
||||
dh->url = MAH_path_to_url (auditor, "/deposit-confirmation");
|
||||
dh->depconf.purpose.size = htonl (sizeof (struct TALER_DepositConfirmationConfirmationPS));
|
||||
dh->depconf.purpose.purpose = htonl (TALER_SIGNATURE_EXCHANGE_DEPOSIT_CONFIRMATION);
|
||||
dh->depconf.h_contract_terms = *h_contract_terms;
|
||||
dh->depconf.h_wire = h_wire;
|
||||
dh->depconf.timestamp = GNUNET_TIME_absolute_hton (timestamp);
|
||||
dh->depconf.refund_deadline = GNUNET_TIME_absolute_hton (refund_deadline);
|
||||
TALER_amount_hton (&dh->depconf.amount_without_fee,
|
||||
&amount_without_fee);
|
||||
dh->depconf.coin_pub = *coin_pub;
|
||||
dh->depconf.merchant = *merchant_pub;
|
||||
dh->amount_with_fee = *amount;
|
||||
dh->coin_value = dki->value;
|
||||
|
||||
eh = TEL_curl_easy_get (dh->url);
|
||||
GNUNET_assert (NULL != (dh->json_enc =
|
||||
json_dumps (deposit_confirmation_obj,
|
||||
JSON_COMPACT)));
|
||||
json_decref (deposit_confirmation_obj);
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"URL for deposit-confirmation: `%s'\n",
|
||||
dh->url);
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_POSTFIELDS,
|
||||
dh->json_enc));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_POSTFIELDSIZE,
|
||||
strlen (dh->json_enc)));
|
||||
ctx = MAH_handle_to_context (auditor);
|
||||
dh->job = GNUNET_CURL_job_add (ctx,
|
||||
eh,
|
||||
GNUNET_YES,
|
||||
(GC_JCC) &handle_deposit_confirmation_finished,
|
||||
dh);
|
||||
return dh;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Cancel a deposit-confirmation permission request. This function cannot be used
|
||||
* on a request handle if a response is already served for it.
|
||||
*
|
||||
* @param deposit-confirmation the deposit-confirmation permission request handle
|
||||
*/
|
||||
void
|
||||
TALER_AUDITOR_deposit_confirmation_cancel (struct TALER_AUDITOR_DepositConfirmationHandle *deposit_confirmation)
|
||||
{
|
||||
if (NULL != deposit_confirmation->job)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (deposit_confirmation->job);
|
||||
deposit_confirmation->job = NULL;
|
||||
}
|
||||
GNUNET_free (deposit_confirmation->url);
|
||||
GNUNET_free (deposit_confirmation->json_enc);
|
||||
GNUNET_free (deposit_confirmation);
|
||||
}
|
||||
|
||||
|
||||
/* end of auditor_api_deposit_confirmation.c */
|
570
src/auditor-lib/auditor_api_handle.c
Normal file
570
src/auditor-lib/auditor_api_handle.c
Normal file
@ -0,0 +1,570 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2018 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* @file auditor-lib/auditor_api_handle.c
|
||||
* @brief Implementation of the "handle" component of the auditor's HTTP API
|
||||
* @author Sree Harsha Totakura <sreeharsha@totakura.in>
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
#include "platform.h"
|
||||
#include <microhttpd.h>
|
||||
#include <gnunet/gnunet_curl_lib.h>
|
||||
#include "taler_json_lib.h"
|
||||
#include "taler_auditor_service.h"
|
||||
#include "taler_signatures.h"
|
||||
#include "auditor_api_handle.h"
|
||||
#include "curl_defaults.h"
|
||||
#include "backoff.h"
|
||||
|
||||
/**
|
||||
* Which revision of the Taler auditor protocol is implemented
|
||||
* by this library? Used to determine compatibility.
|
||||
*/
|
||||
#define TALER_PROTOCOL_CURRENT 0
|
||||
|
||||
/**
|
||||
* How many revisions back are we compatible to?
|
||||
*/
|
||||
#define TALER_PROTOCOL_AGE 0
|
||||
|
||||
|
||||
/**
|
||||
* 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));
|
||||
|
||||
/**
|
||||
* Stages of initialization for the `struct TALER_AUDITOR_Handle`
|
||||
*/
|
||||
enum AuditorHandleState
|
||||
{
|
||||
/**
|
||||
* Just allocated.
|
||||
*/
|
||||
MHS_INIT = 0,
|
||||
|
||||
/**
|
||||
* Obtained the auditor's versioning data and version.
|
||||
*/
|
||||
MHS_VERSION = 1,
|
||||
|
||||
/**
|
||||
* Failed to initialize (fatal).
|
||||
*/
|
||||
MHS_FAILED = 2
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Data for the request to get the /version of a auditor.
|
||||
*/
|
||||
struct VersionRequest;
|
||||
|
||||
|
||||
/**
|
||||
* Handle to the auditor
|
||||
*/
|
||||
struct TALER_AUDITOR_Handle
|
||||
{
|
||||
/**
|
||||
* The context of this handle
|
||||
*/
|
||||
struct GNUNET_CURL_Context *ctx;
|
||||
|
||||
/**
|
||||
* The URL of the auditor (i.e. "http://auditor.taler.net/")
|
||||
*/
|
||||
char *url;
|
||||
|
||||
/**
|
||||
* Function to call with the auditor's certification data,
|
||||
* NULL if this has already been done.
|
||||
*/
|
||||
TALER_AUDITOR_VersionCallback version_cb;
|
||||
|
||||
/**
|
||||
* Closure to pass to @e version_cb.
|
||||
*/
|
||||
void *version_cb_cls;
|
||||
|
||||
/**
|
||||
* Data for the request to get the /version of a auditor,
|
||||
* NULL once we are past stage #MHS_INIT.
|
||||
*/
|
||||
struct VersionRequest *kr;
|
||||
|
||||
/**
|
||||
* Task for retrying /version request.
|
||||
*/
|
||||
struct GNUNET_SCHEDULER_Task *retry_task;
|
||||
|
||||
/**
|
||||
* Key data of the auditor, only valid if
|
||||
* @e handshake_complete is past stage #MHS_VERSION.
|
||||
*/
|
||||
struct TALER_AUDITOR_Version key_data;
|
||||
|
||||
/**
|
||||
* Retry /version frequency.
|
||||
*/
|
||||
struct GNUNET_TIME_Relative retry_delay;
|
||||
|
||||
/**
|
||||
* Stage of the auditor's initialization routines.
|
||||
*/
|
||||
enum AuditorHandleState state;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/* ***************** Internal /version fetching ************* */
|
||||
|
||||
/**
|
||||
* Data for the request to get the /version of a auditor.
|
||||
*/
|
||||
struct VersionRequest
|
||||
{
|
||||
/**
|
||||
* The connection to auditor this request handle will use
|
||||
*/
|
||||
struct TALER_AUDITOR_Handle *auditor;
|
||||
|
||||
/**
|
||||
* The url for this handle
|
||||
*/
|
||||
char *url;
|
||||
|
||||
/**
|
||||
* Entry for this request with the `struct GNUNET_CURL_Context`.
|
||||
*/
|
||||
struct GNUNET_CURL_Job *job;
|
||||
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Release memory occupied by a version request.
|
||||
* Note that this does not cancel the request
|
||||
* itself.
|
||||
*
|
||||
* @param kr request to free
|
||||
*/
|
||||
static void
|
||||
free_version_request (struct VersionRequest *kr)
|
||||
{
|
||||
GNUNET_free (kr->url);
|
||||
GNUNET_free (kr);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Parse a auditor's auditor information encoded in JSON.
|
||||
*
|
||||
* @param[out] auditor where to return the result
|
||||
* @param check_sig should we check signatures
|
||||
* @param[in] auditor_obj json to parse
|
||||
* @param key_data information about denomination version
|
||||
* @return #GNUNET_OK if all is fine, #GNUNET_SYSERR if the signature is
|
||||
* invalid or the json malformed.
|
||||
*/
|
||||
static int
|
||||
parse_json_auditor (struct TALER_AUDITOR_AuditorInformation *auditor,
|
||||
int check_sigs,
|
||||
json_t *auditor_obj,
|
||||
const struct TALER_AUDITOR_Version *key_data)
|
||||
{
|
||||
const char *auditor_url;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_fixed_auto ("auditor_pub",
|
||||
&auditor->auditor_pub),
|
||||
GNUNET_JSON_spec_string ("auditor_url",
|
||||
&auditor_url),
|
||||
GNUNET_JSON_spec_json ("denomination_version",
|
||||
&version),
|
||||
GNUNET_JSON_spec_end()
|
||||
};
|
||||
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (auditor_obj,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
auditor->auditor_url = GNUNET_strdup (auditor_url);
|
||||
GNUNET_JSON_parse_free (spec);
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decode the JSON in @a resp_obj from the /version response and store the data
|
||||
* in the @a key_data.
|
||||
*
|
||||
* @param[in] resp_obj JSON object to parse
|
||||
* @param check_sig #GNUNET_YES if we should check the signature
|
||||
* @param[out] key_data where to store the results we decoded
|
||||
* @param[out] where to store version compatibility data
|
||||
* @return #GNUNET_OK on success, #GNUNET_SYSERR on error (malformed JSON)
|
||||
*/
|
||||
static int
|
||||
decode_version_json (const json_t *resp_obj,
|
||||
int check_sig,
|
||||
struct TALER_AUDITOR_Version *key_data,
|
||||
enum TALER_AUDITOR_VersionCompatibility *vc)
|
||||
{
|
||||
struct TALER_AuditorPublicKeyP pub;
|
||||
unsigned int age;
|
||||
unsigned int revision;
|
||||
unsigned int current;
|
||||
struct GNUNET_JSON_Specification spec[] = {
|
||||
GNUNET_JSON_spec_string ("version",
|
||||
&ver),
|
||||
GNUNET_JSON_spec_fixed_auto ("master_public_key",
|
||||
&key_data->master_pub),
|
||||
GNUNET_JSON_spec_end()
|
||||
};
|
||||
|
||||
if (JSON_OBJECT != json_typeof (resp_obj))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
/* check the version */
|
||||
if (GNUNET_OK !=
|
||||
GNUNET_JSON_parse (resp_obj,
|
||||
spec,
|
||||
NULL, NULL))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
if (3 != sscanf (ver,
|
||||
"%u:%u:%u",
|
||||
¤t,
|
||||
&revision,
|
||||
&age))
|
||||
{
|
||||
GNUNET_break_op (0);
|
||||
return GNUNET_SYSERR;
|
||||
}
|
||||
*vc = TALER_AUDITOR_VC_MATCH;
|
||||
if (TALER_PROTOCOL_CURRENT < current)
|
||||
{
|
||||
*vc |= TALER_AUDITOR_VC_NEWER;
|
||||
if (TALER_PROTOCOL_CURRENT < current - age)
|
||||
*vc |= TALER_AUDITOR_VC_INCOMPATIBLE;
|
||||
}
|
||||
if (TALER_PROTOCOL_CURRENT > current)
|
||||
{
|
||||
*vc |= TALER_AUDITOR_VC_OLDER;
|
||||
if (TALER_PROTOCOL_CURRENT - TALER_PROTOCOL_AGE > current)
|
||||
*vc |= TALER_AUDITOR_VC_INCOMPATIBLE;
|
||||
}
|
||||
key_data->version = GNUNET_strdup (ver);
|
||||
return GNUNET_OK;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Free key data object.
|
||||
*
|
||||
* @param key_data data to free (pointer itself excluded)
|
||||
*/
|
||||
static void
|
||||
free_key_data (struct TALER_AUDITOR_Keys *key_data)
|
||||
{
|
||||
GNUNET_free_non_null (key_data->version);
|
||||
key_data->version = NULL;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initiate download of /version from the auditor.
|
||||
*
|
||||
* @param cls auditor where to download /version from
|
||||
*/
|
||||
static void
|
||||
request_version (void *cls);
|
||||
|
||||
|
||||
/**
|
||||
* Callback used when downloading the reply to a /version request
|
||||
* is complete.
|
||||
*
|
||||
* @param cls the `struct VersionRequest`
|
||||
* @param response_code HTTP response code, 0 on error
|
||||
* @param resp_obj parsed JSON result, NULL on error
|
||||
*/
|
||||
static void
|
||||
version_completed_cb (void *cls,
|
||||
long response_code,
|
||||
const json_t *resp_obj)
|
||||
{
|
||||
struct VersionRequest *kr = cls;
|
||||
struct TALER_AUDITOR_Handle *auditor = kr->auditor;
|
||||
enum TALER_AUDITOR_VersionCompatibility vc;
|
||||
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
|
||||
"Received version from URL `%s' with status %ld.\n",
|
||||
kr->url,
|
||||
response_code);
|
||||
vc = TALER_AUDITOR_VC_PROTOCOL_ERROR;
|
||||
switch (response_code)
|
||||
{
|
||||
case 0:
|
||||
free_version_request (kr);
|
||||
auditor->kr = NULL;
|
||||
GNUNET_assert (NULL == auditor->retry_task);
|
||||
auditor->retry_delay = AUDITOR_LIB_BACKOFF (auditor->retry_delay);
|
||||
auditor->retry_task = GNUNET_SCHEDULER_add_delayed (auditor->retry_delay,
|
||||
&request_version,
|
||||
auditor);
|
||||
return;
|
||||
case MHD_HTTP_OK:
|
||||
if (NULL == resp_obj)
|
||||
{
|
||||
response_code = 0;
|
||||
break;
|
||||
}
|
||||
/* We keep the denomination version and auditor signatures from the
|
||||
previous iteration (/version cherry picking) */
|
||||
if (GNUNET_OK !=
|
||||
decode_version_json (resp_obj,
|
||||
GNUNET_YES,
|
||||
&kd,
|
||||
&vc))
|
||||
{
|
||||
response_code = 0;
|
||||
break;
|
||||
}
|
||||
auditor->retry_delay = GNUNET_TIME_UNIT_ZERO;
|
||||
break;
|
||||
default:
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
|
||||
"Unexpected response code %u\n",
|
||||
(unsigned int) response_code);
|
||||
break;
|
||||
}
|
||||
auditor->key_data = kd;
|
||||
|
||||
if (MHD_HTTP_OK != response_code)
|
||||
{
|
||||
auditor->kr = NULL;
|
||||
free_version_request (kr);
|
||||
auditor->state = MHS_FAILED;
|
||||
free_key_data (&kd_old);
|
||||
/* notify application that we failed */
|
||||
auditor->version_cb (auditor->version_cb_cls,
|
||||
NULL,
|
||||
vc);
|
||||
return;
|
||||
}
|
||||
|
||||
auditor->kr = NULL;
|
||||
free_version_request (kr);
|
||||
auditor->state = MHS_VERSION;
|
||||
/* notify application about the key information */
|
||||
auditor->version_cb (auditor->version_cb_cls,
|
||||
&auditor->key_data,
|
||||
vc);
|
||||
free_key_data (&kd_old);
|
||||
}
|
||||
|
||||
|
||||
/* ********************* library internal API ********* */
|
||||
|
||||
|
||||
/**
|
||||
* Get the context of a auditor.
|
||||
*
|
||||
* @param h the auditor handle to query
|
||||
* @return ctx context to execute jobs in
|
||||
*/
|
||||
struct GNUNET_CURL_Context *
|
||||
MAH_handle_to_context (struct TALER_AUDITOR_Handle *h)
|
||||
{
|
||||
return h->ctx;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Check if the handle is ready to process requests.
|
||||
*
|
||||
* @param h the auditor handle to query
|
||||
* @return #GNUNET_YES if we are ready, #GNUNET_NO if not
|
||||
*/
|
||||
int
|
||||
MAH_handle_is_ready (struct TALER_AUDITOR_Handle *h)
|
||||
{
|
||||
return (MHS_VERSION == h->state) ? GNUNET_YES : GNUNET_NO;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the URL to use for an API request.
|
||||
*
|
||||
* @param h handle for the auditor
|
||||
* @param path Taler API path (i.e. "/reserve/withdraw")
|
||||
* @return the full URL to use with cURL
|
||||
*/
|
||||
char *
|
||||
MAH_path_to_url (struct TALER_AUDITOR_Handle *h,
|
||||
const char *path)
|
||||
{
|
||||
return MAH_path_to_url2 (h->url,
|
||||
path);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the URL to use for an API request.
|
||||
*
|
||||
* @param base_url base URL of the auditor (i.e. "http://auditor/")
|
||||
* @param path Taler API path (i.e. "/reserve/withdraw")
|
||||
* @return the full URL to use with cURL
|
||||
*/
|
||||
char *
|
||||
MAH_path_to_url2 (const char *base_url,
|
||||
const char *path)
|
||||
{
|
||||
char *url;
|
||||
|
||||
if ( ('/' == path[0]) &&
|
||||
(0 < strlen (base_url)) &&
|
||||
('/' == base_url[strlen (base_url) - 1]) )
|
||||
path++; /* avoid generating URL with "//" from concat */
|
||||
GNUNET_asprintf (&url,
|
||||
"%s%s",
|
||||
base_url,
|
||||
path);
|
||||
return url;
|
||||
}
|
||||
|
||||
|
||||
/* ********************* public API ******************* */
|
||||
|
||||
|
||||
/**
|
||||
* Initialise a connection to the auditor. Will connect to the
|
||||
* auditor and obtain information about the auditor's master public
|
||||
* key and the auditor's auditor. The respective information will
|
||||
* be passed to the @a version_cb once available, and all future
|
||||
* interactions with the auditor will be checked to be signed
|
||||
* (where appropriate) by the respective master key.
|
||||
*
|
||||
* @param ctx the context
|
||||
* @param url HTTP base URL for the auditor
|
||||
* @param version_cb function to call with the auditor's versionification information
|
||||
* @param version_cb_cls closure for @a version_cb
|
||||
* @return the auditor handle; NULL upon error
|
||||
*/
|
||||
struct TALER_AUDITOR_Handle *
|
||||
TALER_AUDITOR_connect (struct GNUNET_CURL_Context *ctx,
|
||||
const char *url,
|
||||
TALER_AUDITOR_VersionificationCallback version_cb,
|
||||
void *version_cb_cls)
|
||||
{
|
||||
struct TALER_AUDITOR_Handle *auditor;
|
||||
|
||||
auditor = GNUNET_new (struct TALER_AUDITOR_Handle);
|
||||
auditor->ctx = ctx;
|
||||
auditor->url = GNUNET_strdup (url);
|
||||
auditor->version_cb = version_cb;
|
||||
auditor->version_cb_cls = version_cb_cls;
|
||||
auditor->retry_task = GNUNET_SCHEDULER_add_now (&request_version,
|
||||
auditor);
|
||||
return auditor;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Initiate download of /version from the auditor.
|
||||
*
|
||||
* @param cls auditor where to download /version from
|
||||
*/
|
||||
static void
|
||||
request_version (void *cls)
|
||||
{
|
||||
struct TALER_AUDITOR_Handle *auditor = cls;
|
||||
struct VersionRequest *kr;
|
||||
CURL *eh;
|
||||
|
||||
auditor->retry_task = NULL;
|
||||
GNUNET_assert (NULL == auditor->kr);
|
||||
kr = GNUNET_new (struct VersionRequest);
|
||||
kr->auditor = auditor;
|
||||
kr->url = MAH_path_to_url (auditor,
|
||||
"/version");
|
||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||
"Requesting version with URL `%s'.\n",
|
||||
kr->url);
|
||||
eh = TEL_curl_easy_get (kr->url);
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_VERBOSE,
|
||||
0));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_TIMEOUT,
|
||||
(long) 300));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_HEADERDATA,
|
||||
kr));
|
||||
kr->job = GNUNET_CURL_job_add (auditor->ctx,
|
||||
eh,
|
||||
GNUNET_NO,
|
||||
(GC_JCC) &version_completed_cb,
|
||||
kr);
|
||||
auditor->kr = kr;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Disconnect from the auditor
|
||||
*
|
||||
* @param auditor the auditor handle
|
||||
*/
|
||||
void
|
||||
TALER_AUDITOR_disconnect (struct TALER_AUDITOR_Handle *auditor)
|
||||
{
|
||||
if (NULL != auditor->kr)
|
||||
{
|
||||
GNUNET_CURL_job_cancel (auditor->kr->job);
|
||||
free_version_request (auditor->kr);
|
||||
auditor->kr = NULL;
|
||||
}
|
||||
free_key_data (&auditor->key_data);
|
||||
if (NULL != auditor->retry_task)
|
||||
{
|
||||
GNUNET_SCHEDULER_cancel (auditor->retry_task);
|
||||
auditor->retry_task = NULL;
|
||||
}
|
||||
GNUNET_free (auditor->url);
|
||||
GNUNET_free (auditor);
|
||||
}
|
||||
|
||||
|
||||
/* end of auditor_api_handle.c */
|
71
src/auditor-lib/auditor_api_handle.h
Normal file
71
src/auditor-lib/auditor_api_handle.h
Normal file
@ -0,0 +1,71 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014, 2015 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* @file auditor-lib/auditor_api_handle.h
|
||||
* @brief Internal interface to the handle part of the auditor's HTTP API
|
||||
* @author Christian Grothoff
|
||||
*/
|
||||
#include "platform.h"
|
||||
#include <gnunet/gnunet_curl_lib.h>
|
||||
#include "taler_auditor_service.h"
|
||||
|
||||
|
||||
/**
|
||||
* Get the context of a auditor.
|
||||
*
|
||||
* @param h the auditor handle to query
|
||||
* @return ctx context to execute jobs in
|
||||
*/
|
||||
struct GNUNET_CURL_Context *
|
||||
MAH_handle_to_context (struct TALER_AUDITOR_Handle *h);
|
||||
|
||||
|
||||
/**
|
||||
* Check if the handle is ready to process requests.
|
||||
*
|
||||
* @param h the auditor handle to query
|
||||
* @return #GNUNET_YES if we are ready, #GNUNET_NO if not
|
||||
*/
|
||||
int
|
||||
MAH_handle_is_ready (struct TALER_AUDITOR_Handle *h);
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the URL to use for an API request.
|
||||
*
|
||||
* @param h the auditor handle to query
|
||||
* @param path Taler API path (i.e. "/reserve/withdraw")
|
||||
* @return the full URL to use with cURL
|
||||
*/
|
||||
char *
|
||||
MAH_path_to_url (struct TALER_AUDITOR_Handle *h,
|
||||
const char *path);
|
||||
|
||||
|
||||
/**
|
||||
* Obtain the URL to use for an API request.
|
||||
*
|
||||
* @param base_url base URL of the auditor (i.e. "http://auditor/")
|
||||
* @param path Taler API path (i.e. "/reserve/withdraw")
|
||||
* @return the full URL to use with cURL
|
||||
*/
|
||||
char *
|
||||
MAH_path_to_url2 (const char *base_url,
|
||||
const char *path);
|
||||
|
||||
|
||||
/* end of auditor_api_handle.h */
|
74
src/auditor-lib/curl_defaults.c
Normal file
74
src/auditor-lib/curl_defaults.c
Normal file
@ -0,0 +1,74 @@
|
||||
/*
|
||||
This file is part of TALER
|
||||
Copyright (C) 2014-2018 GNUnet e.V.
|
||||
|
||||
TALER is free software; you can redistribute it and/or modify it under the
|
||||
terms of the GNU General Public License as published by the Free Software
|
||||
Foundation; either version 3, or (at your option) any later version.
|
||||
|
||||
TALER is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
|
||||
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License along with
|
||||
TALER; see the file COPYING. If not, see
|
||||
<http://www.gnu.org/licenses/>
|
||||
*/
|
||||
/**
|
||||
* @file auditor-lib/curl_defaults.c
|
||||
* @brief curl easy handle defaults
|
||||
* @author Florian Dold
|
||||
*/
|
||||
|
||||
#include "curl_defaults.h"
|
||||
|
||||
|
||||
/**
|
||||
* Get a curl handle with the right defaults
|
||||
* for the exchange lib. In the future, we might manage a pool of connections here.
|
||||
*
|
||||
* @param url URL to query
|
||||
*/
|
||||
CURL *
|
||||
TEL_curl_easy_get (char *url)
|
||||
{
|
||||
CURL *eh;
|
||||
|
||||
eh = curl_easy_init ();
|
||||
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_URL,
|
||||
url));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_ENCODING,
|
||||
"deflate"));
|
||||
GNUNET_assert (CURLE_OK ==
|
||||
curl_easy_setopt (eh,
|
||||
CURLOPT_TCP_FASTOPEN,
|
||||
1L));
|
||||
|
||||
{
|
||||
/* Unfortunately libcurl needs chunk to be alive until after
|
||||
curl_easy_perform. To avoid manual cleanup, we keep
|
||||
one static list here. */
|
||||
static struct curl_slist *chunk = NULL;
|
||||
if (NULL == chunk)
|
||||
{
|
||||
/* With POST requests, we do not want to wait for the
|
||||
"100 Continue" response, as our request bodies are usually
|
||||
small and directy sending them saves us a round trip.
|
||||
|
||||
Clearing the expect header like this disables libcurl's
|
||||
default processing of the header.
|
||||
|
||||
Disabling this header is safe for other HTTP methods, thus
|
||||
we don't distinguish further before setting it. */
|
||||
chunk = curl_slist_append (chunk, "Expect:");
|
||||
}
|
||||
GNUNET_assert (CURLE_OK == curl_easy_setopt (eh, CURLOPT_HTTPHEADER, chunk));
|
||||
}
|
||||
|
||||
return eh;
|
||||
}
|
Loading…
Reference in New Issue
Block a user