summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rwxr-xr-xsrc/auditor/generate-auditor-basedb.sh1
-rw-r--r--src/auditor/taler-auditor-httpd.c2
-rwxr-xr-xsrc/auditor/test-auditor.sh73
-rw-r--r--src/auditordb/Makefile.am4
-rw-r--r--src/bank-lib/Makefile.am4
-rw-r--r--src/curl/Makefile.am2
-rw-r--r--src/exchange/taler-exchange-httpd_config.c34
-rw-r--r--src/exchange/taler-exchange-httpd_config.h2
-rw-r--r--src/exchange/taler-exchange-httpd_keys.c27
-rw-r--r--src/exchange/taler-exchange-httpd_melt.c1
-rw-r--r--src/exchange/taler-exchange-httpd_refund.c1
-rw-r--r--src/exchange/taler-exchange-httpd_transfers_get.c146
-rw-r--r--src/exchangedb/0002-kyc_attributes.sql10
-rw-r--r--src/exchangedb/0004-refunds.sql35
-rw-r--r--src/exchangedb/Makefile.am16
-rw-r--r--src/exchangedb/exchange-0004.sql.in24
-rw-r--r--src/include/taler_crypto_lib.h10
-rw-r--r--src/include/taler_exchangedb_plugin.h15
-rw-r--r--src/include/taler_mhd_lib.h272
-rw-r--r--src/include/taler_pq_lib.h25
-rw-r--r--src/lib/Makefile.am6
-rw-r--r--src/lib/exchange_api_handle.c4
-rw-r--r--src/lib/exchange_api_refund.c4
-rw-r--r--src/lib/exchange_api_transfers_get.c22
-rw-r--r--src/mhd/Makefile.am2
-rw-r--r--src/mhd/mhd_legal.c11
-rw-r--r--src/mhd/mhd_parsing.c62
-rw-r--r--src/pq/pq_query_helper.c200
-rw-r--r--src/pq/pq_result_helper.c5
-rw-r--r--src/templating/.gitignore1
-rw-r--r--src/templating/Makefile.am71
-rwxr-xr-xsrc/templating/dotest.sh22
-rw-r--r--src/templating/mustach-original-Makefile9
-rwxr-xr-xsrc/templating/run-original-tests.sh14
-rw-r--r--src/templating/templating_api.c13
-rw-r--r--src/templating/test7/Makefile8
-rw-r--r--src/templating/test8/Makefile8
-rwxr-xr-xsrc/testing/taler-unified-setup.sh5
-rw-r--r--src/util/Makefile.am2
-rw-r--r--src/util/crypto_confirmation.c7
-rw-r--r--src/util/payto.c72
-rw-r--r--src/util/test_payto.c39
42 files changed, 752 insertions, 539 deletions
diff --git a/src/auditor/generate-auditor-basedb.sh b/src/auditor/generate-auditor-basedb.sh
index 65aae3134..bbce37cdc 100755
--- a/src/auditor/generate-auditor-basedb.sh
+++ b/src/auditor/generate-auditor-basedb.sh
@@ -119,7 +119,6 @@ taler-wallet-cli \
echo " DONE"
taler-wallet-cli --wallet-db="$WALLET_DB" run-until-done
-taler-wallet-cli --wallet-db="$WALLET_DB" advanced run-pending
# Dump database
mkdir -p "$(dirname "$BASEDB")"
diff --git a/src/auditor/taler-auditor-httpd.c b/src/auditor/taler-auditor-httpd.c
index d9cdff8d2..59bd849bc 100644
--- a/src/auditor/taler-auditor-httpd.c
+++ b/src/auditor/taler-auditor-httpd.c
@@ -169,7 +169,7 @@ handle_config (struct TAH_RequestHandler *rh,
GNUNET_JSON_pack_string ("version",
AUDITOR_PROTOCOL_VERSION),
GNUNET_JSON_pack_string ("implementation",
- "urn:net:taler:specs:auditor:c-reference"),
+ "urn:net:taler:specs:taler-auditor:c-reference"),
GNUNET_JSON_pack_string ("currency",
TAH_currency),
GNUNET_JSON_pack_data_auto ("auditor_public_key",
diff --git a/src/auditor/test-auditor.sh b/src/auditor/test-auditor.sh
index ea8637cfa..2cfea0532 100755
--- a/src/auditor/test-auditor.sh
+++ b/src/auditor/test-auditor.sh
@@ -166,7 +166,7 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-aggregation.out" \
2> "${MY_TMP_DIR}/test-audit-aggregation.err" \
- || exit_fail "aggregation audit failed"
+ || exit_fail "aggregation audit failed (see ${MY_TMP_DIR}/test-audit-aggregation.*)"
echo -n "."
$VALGRIND taler-helper-auditor-aggregation \
-L DEBUG \
@@ -174,7 +174,7 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-aggregation-inc.out" \
2> "${MY_TMP_DIR}/test-audit-aggregation-inc.err" \
- || exit_fail "incremental aggregation audit failed"
+ || exit_fail "incremental aggregation audit failed (see ${MY_TMP_DIR}/test-audit-aggregation-inc.*)"
echo -n "."
$VALGRIND taler-helper-auditor-coins \
-L DEBUG \
@@ -182,7 +182,7 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-coins.out" \
2> "${MY_TMP_DIR}/test-audit-coins.err" \
- || exit_fail "coin audit failed"
+ || exit_fail "coin audit failed (see ${MY_TMP_DIR}/test-audit-coins.*)"
echo -n "."
$VALGRIND taler-helper-auditor-coins \
-L DEBUG \
@@ -190,7 +190,7 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-coins-inc.out" \
2> "${MY_TMP_DIR}/test-audit-coins-inc.err" \
- || exit_fail "incremental coin audit failed"
+ || exit_fail "incremental coin audit failed (see ${MY_TMP_DIR}/test-audit-coins-inc.*)"
echo -n "."
$VALGRIND taler-helper-auditor-deposits \
-L DEBUG \
@@ -198,7 +198,7 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-deposits.out" \
2> "${MY_TMP_DIR}/test-audit-deposits.err" \
- || exit_fail "deposits audit failed"
+ || exit_fail "deposits audit failed (see ${MY_TMP_DIR}/test-audit-deposits.*)"
echo -n "."
$VALGRIND taler-helper-auditor-deposits \
-L DEBUG \
@@ -206,7 +206,7 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-deposits-inc.out" \
2> "${MY_TMP_DIR}/test-audit-deposits-inc.err" \
- || exit_fail "incremental deposits audit failed"
+ || exit_fail "incremental deposits audit failed (see ${MY_TMP_DIR}/test-audit-deposits-inc.*)"
echo -n "."
$VALGRIND taler-helper-auditor-reserves \
-i \
@@ -215,7 +215,7 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-reserves.out" \
2> "${MY_TMP_DIR}/test-audit-reserves.err" \
- || exit_fail "reserves audit failed"
+ || exit_fail "reserves audit failed (see ${MY_TMP_DIR}/test-audit-reserves.*)"
echo -n "."
$VALGRIND taler-helper-auditor-reserves \
-i \
@@ -224,25 +224,25 @@ function audit_only () {
-t \
> "${MY_TMP_DIR}/test-audit-reserves-inc.out" \
2> "${MY_TMP_DIR}/test-audit-reserves-inc.err" \
- || exit_fail "incremental reserves audit failed"
+ || exit_fail "incremental reserves audit failed (see ${MY_TMP_DIR}/test-audit-reserves-inc.*)"
echo -n "."
$VALGRIND taler-helper-auditor-wire \
-i \
-L DEBUG \
-c "$CONF" \
-t \
- > "${MY_TMP_DIR}/test-wire-audit.out" \
- 2> "${MY_TMP_DIR}/test-wire-audit.err" \
- || exit_fail "wire audit failed"
+ > "${MY_TMP_DIR}/test-audit-wire.out" \
+ 2> "${MY_TMP_DIR}/test-audit-wire.err" \
+ || exit_fail "wire audit failed (see ${MY_TMP_DIR}/test-audit-wire.*)"
echo -n "."
$VALGRIND taler-helper-auditor-wire \
-i \
-L DEBUG \
-c "$CONF" \
-t \
- > "${MY_TMP_DIR}/test-wire-audit-inc.out" \
- 2> "${MY_TMP_DIR}/test-wire-audit-inc.err" \
- || exit_fail "wire audit inc failed"
+ > "${MY_TMP_DIR}/test-audit-wire-inc.out" \
+ 2> "${MY_TMP_DIR}/test-audit-wire-inc.err" \
+ || exit_fail "wire audit inc failed (see ${MY_TMP_DIR}/test-audit-wire-inc.*)"
echo -n "."
echo " DONE"
@@ -2211,32 +2211,45 @@ grep -v host \
> "$TMPDIR/pg_hba.conf.new"
mv "$TMPDIR/pg_hba.conf.new" "$TMPDIR/pg_hba.conf"
"${POSTGRES_PATH}/pg_ctl" \
- -D "$TMPDIR" \
- -l "${MY_TMP_DIR}/postgres.log" \
- start \
- > "${MY_TMP_DIR}/postgres-start.log" \
- 2> "${MY_TMP_DIR}/postgres-start.err"
+ -D "$TMPDIR" \
+ -l "${MY_TMP_DIR}/postgres.log" \
+ start \
+ > "${MY_TMP_DIR}/postgres-start.log" \
+ 2> "${MY_TMP_DIR}/postgres-start.err"
echo " DONE"
PGHOST="$TMPDIR/sockets"
export PGHOST
MYDIR="${MY_TMP_DIR}/basedb"
mkdir -p "${MYDIR}"
-echo "Generating fresh database at $MYDIR"
-if faketime -f '-1 d' ./generate-auditor-basedb.sh -d "$MYDIR/$DB"
+if [ -z $REUSE_BASEDB_DIR ]
then
- echo -n "Reset 'auditor-basedb' database at $PGHOST ..."
- dropdb "auditor-basedb" >/dev/null 2>/dev/null || true
- createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB' at $PGHOST"
- echo " DONE"
- check_with_database "$MYDIR/$DB"
- if [ "$fail" != "0" ]
+ echo "Generating fresh database at $MYDIR"
+
+ if faketime -f '-1 d' ./generate-auditor-basedb.sh -d "$MYDIR/$DB"
then
- exit "$fail"
+ echo -n "Reset 'auditor-basedb' database at $PGHOST ..."
+ dropdb "auditor-basedb" >/dev/null 2>/dev/null || true
+ createdb "auditor-basedb" || exit_skip "Could not create database '$BASEDB' at $PGHOST"
+ echo " DONE"
+ else
+ echo "Generation failed"
+ exit 1
fi
else
- echo "Generation failed"
- exit 1
+ echo "Reusing existing database from ${REUSE_BASEDB_DIR}"
+ cp -r "${REUSE_BASEDB_DIR}/basedb"/* "${MYDIR}/"
+fi
+
+check_with_database "$MYDIR/$DB"
+if [ "$fail" != "0" ]
+then
+ exit "$fail"
+fi
+
+if [ -z $REUSE_BASEDB_DIR ]
+then
+ echo "Run 'export REUSE_BASEDB_DIR=${MY_TMP_DIR}' to re-run tests against the same database"
fi
exit 0
diff --git a/src/auditordb/Makefile.am b/src/auditordb/Makefile.am
index 3e7d83d09..c0282e9c9 100644
--- a/src/auditordb/Makefile.am
+++ b/src/auditordb/Makefile.am
@@ -31,12 +31,12 @@ CLEANFILES = \
auditor-0002.sql
procedures.sql: procedures.sql.in auditor_do_*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < procedures.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
auditor-0002.sql: auditor-0002.sql.in 0002-*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < auditor-0002.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
diff --git a/src/bank-lib/Makefile.am b/src/bank-lib/Makefile.am
index b849db5d8..a292dcece 100644
--- a/src/bank-lib/Makefile.am
+++ b/src/bank-lib/Makefile.am
@@ -51,7 +51,7 @@ libtalerbank_la_LIBADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
- $(LIBGNURLCURL_LIBS) \
+ -lcurl \
$(XLIB)
libtalerfakebank_la_LDFLAGS = \
@@ -94,7 +94,7 @@ libtalerfakebank_la_LIBADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
- $(LIBGNURLCURL_LIBS) \
+ -lcurl \
-lmicrohttpd \
-lpthread \
$(XLIB)
diff --git a/src/curl/Makefile.am b/src/curl/Makefile.am
index f60a3806d..c8f8761aa 100644
--- a/src/curl/Makefile.am
+++ b/src/curl/Makefile.am
@@ -17,7 +17,7 @@ libtalercurl_la_SOURCES = \
libtalercurl_la_LIBADD = \
-lgnunetcurl \
-lgnunetutil \
- $(LIBGNURLCURL_LIBS) \
+ -lcurl \
-ljansson \
-lz \
-lm \
diff --git a/src/exchange/taler-exchange-httpd_config.c b/src/exchange/taler-exchange-httpd_config.c
index 29cbf2b51..257dfa6ba 100644
--- a/src/exchange/taler-exchange-httpd_config.c
+++ b/src/exchange/taler-exchange-httpd_config.c
@@ -33,9 +33,31 @@ TEH_handler_config (struct TEH_RequestContext *rc,
const char *const args[])
{
static struct MHD_Response *resp;
+ static struct GNUNET_TIME_Absolute a;
+ (void) args;
+ if ( (GNUNET_TIME_absolute_is_past (a)) &&
+ (NULL != resp) )
+ {
+ MHD_destroy_response (resp);
+ resp = NULL;
+ }
if (NULL == resp)
{
+ struct GNUNET_TIME_Timestamp km;
+ char dat[128];
+
+ a = GNUNET_TIME_relative_to_absolute (GNUNET_TIME_UNIT_DAYS);
+ /* Round up to next full day to ensure the expiration
+ time does not become a fingerprint! */
+ a = GNUNET_TIME_absolute_round_down (a,
+ GNUNET_TIME_UNIT_DAYS);
+ a = GNUNET_TIME_absolute_add (a,
+ GNUNET_TIME_UNIT_DAYS);
+ /* => /config response stays at most 48h in caches! */
+ km = GNUNET_TIME_absolute_to_timestamp (a);
+ TALER_MHD_get_date_string (km.abs_time,
+ dat);
resp = TALER_MHD_MAKE_JSON_PACK (
GNUNET_JSON_pack_array_steal ("supported_kyc_requirements",
TALER_KYCLOGIC_get_satisfiable ()),
@@ -47,9 +69,19 @@ TEH_handler_config (struct TEH_RequestContext *rc,
GNUNET_JSON_pack_string ("name",
"taler-exchange"),
GNUNET_JSON_pack_string ("implementation",
- "urn:net:taler:specs:exchange:c-reference"),
+ "urn:net:taler:specs:taler-exchange:c-reference")
+ ,
GNUNET_JSON_pack_string ("version",
EXCHANGE_PROTOCOL_VERSION));
+
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_EXPIRES,
+ dat));
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (resp,
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "public,max-age=21600")); /* 6h */
}
return MHD_queue_response (rc->connection,
MHD_HTTP_OK,
diff --git a/src/exchange/taler-exchange-httpd_config.h b/src/exchange/taler-exchange-httpd_config.h
index 40293e89a..068f51d41 100644
--- a/src/exchange/taler-exchange-httpd_config.h
+++ b/src/exchange/taler-exchange-httpd_config.h
@@ -41,7 +41,7 @@
*
* Returned via both /config and /keys endpoints.
*/
-#define EXCHANGE_PROTOCOL_VERSION "18:1:1"
+#define EXCHANGE_PROTOCOL_VERSION "19:2:2"
/**
diff --git a/src/exchange/taler-exchange-httpd_keys.c b/src/exchange/taler-exchange-httpd_keys.c
index 05fb685e0..0ec28e950 100644
--- a/src/exchange/taler-exchange-httpd_keys.c
+++ b/src/exchange/taler-exchange-httpd_keys.c
@@ -2273,12 +2273,10 @@ setup_general_response_headers (void *cls,
MHD_add_response_header (response,
MHD_HTTP_HEADER_CONTENT_TYPE,
"application/json"));
- TALER_MHD_get_date_string (ksh->reload_time.abs_time,
- dat);
GNUNET_break (MHD_YES ==
MHD_add_response_header (response,
- MHD_HTTP_HEADER_LAST_MODIFIED,
- dat));
+ MHD_HTTP_HEADER_CACHE_CONTROL,
+ "public,must-revalidate,max-age=86400"));
if (! GNUNET_TIME_relative_is_zero (ksh->rekey_frequency))
{
struct GNUNET_TIME_Relative r;
@@ -2290,6 +2288,12 @@ setup_general_response_headers (void *cls,
r = GNUNET_TIME_relative_min (TEH_max_keys_caching,
ksh->rekey_frequency);
a = GNUNET_TIME_relative_to_absolute (r);
+ /* Round up to next full day to ensure the expiration
+ time does not become a fingerprint! */
+ a = GNUNET_TIME_absolute_round_down (a,
+ GNUNET_TIME_UNIT_DAYS);
+ a = GNUNET_TIME_absolute_add (a,
+ GNUNET_TIME_UNIT_DAYS);
km = GNUNET_TIME_absolute_to_timestamp (a);
we = GNUNET_TIME_absolute_to_timestamp (wire_state->cache_expiration);
m = GNUNET_TIME_timestamp_min (we,
@@ -2314,11 +2318,6 @@ setup_general_response_headers (void *cls,
MHD_add_response_header (response,
MHD_HTTP_HEADER_VARY,
MHD_HTTP_HEADER_ACCEPT_ENCODING));
- /* Information is always public, revalidate after 1 hour */
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (response,
- MHD_HTTP_HEADER_CACHE_CONTROL,
- "public,max-age=3600"));
}
@@ -2596,6 +2595,7 @@ create_krd (struct TEH_KeyStateHandle *ksh,
GNUNET_assert (NULL != krd.response_uncompressed);
setup_general_response_headers (ksh,
krd.response_uncompressed);
+ /* Information is always public, revalidate after 1 day */
GNUNET_break (MHD_YES ==
MHD_add_response_header (krd.response_uncompressed,
MHD_HTTP_HEADER_ETAG,
@@ -2617,18 +2617,9 @@ create_krd (struct TEH_KeyStateHandle *ksh,
"deflate")) );
setup_general_response_headers (ksh,
krd.response_compressed);
- /* Set cache control headers: our response varies depending on these headers */
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (krd.response_compressed,
- MHD_HTTP_HEADER_VARY,
- MHD_HTTP_HEADER_ACCEPT_ENCODING));
/* Information is always public, revalidate after 1 day */
GNUNET_break (MHD_YES ==
MHD_add_response_header (krd.response_compressed,
- MHD_HTTP_HEADER_CACHE_CONTROL,
- "public,max-age=86400"));
- GNUNET_break (MHD_YES ==
- MHD_add_response_header (krd.response_compressed,
MHD_HTTP_HEADER_ETAG,
etag));
krd.etag = GNUNET_strdup (etag);
diff --git a/src/exchange/taler-exchange-httpd_melt.c b/src/exchange/taler-exchange-httpd_melt.c
index b31078f00..ac3902e3f 100644
--- a/src/exchange/taler-exchange-httpd_melt.c
+++ b/src/exchange/taler-exchange-httpd_melt.c
@@ -58,6 +58,7 @@ reply_melt_success (struct MHD_Connection *connection,
&pub,
&sig)))
{
+ GNUNET_break (0);
return TALER_MHD_reply_with_ec (connection,
ec,
NULL);
diff --git a/src/exchange/taler-exchange-httpd_refund.c b/src/exchange/taler-exchange-httpd_refund.c
index 33ead7c69..b8bcf7c60 100644
--- a/src/exchange/taler-exchange-httpd_refund.c
+++ b/src/exchange/taler-exchange-httpd_refund.c
@@ -158,6 +158,7 @@ refund_transaction (void *cls,
}
if (conflict)
{
+ GNUNET_break_op (0);
*mhd_ret = TEH_RESPONSE_reply_coin_insufficient_funds (
connection,
TALER_EC_EXCHANGE_REFUND_INCONSISTENT_AMOUNT,
diff --git a/src/exchange/taler-exchange-httpd_transfers_get.c b/src/exchange/taler-exchange-httpd_transfers_get.c
index 2a6dc8776..18d96f955 100644
--- a/src/exchange/taler-exchange-httpd_transfers_get.c
+++ b/src/exchange/taler-exchange-httpd_transfers_get.c
@@ -59,14 +59,20 @@ struct AggregatedDepositDetail
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
- * Total value of the coin in the deposit.
+ * Total value of the coin in the deposit (after
+ * refunds).
*/
struct TALER_Amount deposit_value;
/**
- * Fees charged by the exchange for the deposit of this coin.
+ * Fees charged by the exchange for the deposit of this coin (possibly after reduction due to refunds).
*/
struct TALER_Amount deposit_fee;
+
+ /**
+ * Total amount refunded for this coin.
+ */
+ struct TALER_Amount refund_total;
};
@@ -120,6 +126,13 @@ reply_transfer_details (struct MHD_Connection *connection,
&wdd_pos->h_contract_terms),
GNUNET_JSON_pack_data_auto ("coin_pub",
&wdd_pos->coin_pub),
+
+ GNUNET_JSON_pack_allow_null (
+ TALER_JSON_pack_amount ("refund_total",
+ TALER_amount_is_zero (
+ &wdd_pos->refund_total)
+ ? NULL
+ : &wdd_pos->refund_total)),
TALER_JSON_pack_amount ("deposit_value",
&wdd_pos->deposit_value),
TALER_JSON_pack_amount ("deposit_fee",
@@ -248,6 +261,31 @@ struct WtidTransactionContext
/**
+ * Callback that totals up the applicable refunds.
+ *
+ * @param cls a `struct TALER_Amount` where we keep the total
+ * @param amount_with_fee amount being refunded
+ */
+static enum GNUNET_GenericReturnValue
+add_refunds (void *cls,
+ const struct TALER_Amount *amount_with_fee)
+
+{
+ struct TALER_Amount *total = cls;
+
+ if (0 >
+ TALER_amount_add (total,
+ total,
+ amount_with_fee))
+ {
+ GNUNET_break (0);
+ return GNUNET_SYSERR;
+ }
+ return GNUNET_OK;
+}
+
+
+/**
* Function called with the results of the lookup of the individual deposits
* that were aggregated for the given wire transfer.
*
@@ -260,29 +298,96 @@ struct WtidTransactionContext
* @param h_contract_terms which proposal was this payment about
* @param denom_pub denomination public key of the @a coin_pub (ignored)
* @param coin_pub which public key was this payment about
- * @param deposit_value amount contributed by this coin in total
+ * @param deposit_value amount contributed by this coin in total (including fee)
* @param deposit_fee deposit fee charged by exchange for this coin
*/
static void
-handle_deposit_data (void *cls,
- uint64_t rowid,
- const struct TALER_MerchantPublicKeyP *merchant_pub,
- const char *account_payto_uri,
- const struct TALER_PaytoHashP *h_payto,
- struct GNUNET_TIME_Timestamp exec_time,
- const struct TALER_PrivateContractHashP *h_contract_terms,
- const struct TALER_DenominationPublicKey *denom_pub,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct TALER_Amount *deposit_value,
- const struct TALER_Amount *deposit_fee)
+handle_deposit_data (
+ void *cls,
+ uint64_t rowid,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const char *account_payto_uri,
+ const struct TALER_PaytoHashP *h_payto,
+ struct GNUNET_TIME_Timestamp exec_time,
+ const struct TALER_PrivateContractHashP *h_contract_terms,
+ const struct TALER_DenominationPublicKey *denom_pub,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_Amount *deposit_value,
+ const struct TALER_Amount *deposit_fee)
{
struct WtidTransactionContext *ctx = cls;
+ struct TALER_Amount total_refunds;
+ struct TALER_Amount dval;
+ struct TALER_Amount dfee;
+ enum GNUNET_DB_QueryStatus qs;
(void) rowid;
(void) denom_pub;
(void) h_payto;
if (GNUNET_SYSERR == ctx->is_valid)
return;
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (deposit_value->currency,
+ &total_refunds));
+ qs = TEH_plugin->select_refunds_by_coin (TEH_plugin->cls,
+ coin_pub,
+ merchant_pub,
+ h_contract_terms,
+ &add_refunds,
+ &total_refunds);
+ if (qs < 0)
+ {
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ if (1 ==
+ TALER_amount_cmp (&total_refunds,
+ deposit_value))
+ {
+ /* Refunds exceeded total deposit? not OK! */
+ GNUNET_break (0);
+ ctx->is_valid = GNUNET_SYSERR;
+ return;
+ }
+ if (0 ==
+ TALER_amount_cmp (&total_refunds,
+ deposit_value))
+ {
+ /* total_refunds == deposit_value;
+ in this case, the total contributed to the
+ wire transfer is zero (as are fees) */
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (deposit_value->currency,
+ &dval));
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (deposit_value->currency,
+ &dfee));
+
+ }
+ else
+ {
+ /* Compute deposit value by subtracting refunds */
+ GNUNET_assert (0 <
+ TALER_amount_subtract (&dval,
+ deposit_value,
+ &total_refunds));
+ if (-1 ==
+ TALER_amount_cmp (&dval,
+ deposit_fee))
+ {
+ /* dval < deposit_fee, so after refunds less than
+ the deposit fee remains; reduce deposit fee to
+ the remaining value of the coin */
+ dfee = dval;
+ }
+ else
+ {
+ /* Partial refund, deposit fee remains */
+ dfee = *deposit_fee;
+ }
+ }
+
if (GNUNET_NO == ctx->is_valid)
{
/* First one we encounter, setup general information in 'ctx' */
@@ -292,8 +397,8 @@ handle_deposit_data (void *cls,
ctx->is_valid = GNUNET_YES;
if (0 >
TALER_amount_subtract (&ctx->total,
- deposit_value,
- deposit_fee))
+ &dval,
+ &dfee))
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
@@ -317,8 +422,8 @@ handle_deposit_data (void *cls,
}
if (0 >
TALER_amount_subtract (&delta,
- deposit_value,
- deposit_fee))
+ &dval,
+ &dfee))
{
GNUNET_break (0);
ctx->is_valid = GNUNET_SYSERR;
@@ -339,8 +444,9 @@ handle_deposit_data (void *cls,
struct AggregatedDepositDetail *wdd;
wdd = GNUNET_new (struct AggregatedDepositDetail);
- wdd->deposit_value = *deposit_value;
- wdd->deposit_fee = *deposit_fee;
+ wdd->deposit_value = dval;
+ wdd->deposit_fee = dfee;
+ wdd->refund_total = total_refunds;
wdd->h_contract_terms = *h_contract_terms;
wdd->coin_pub = *coin_pub;
GNUNET_CONTAINER_DLL_insert (ctx->wdd_head,
diff --git a/src/exchangedb/0002-kyc_attributes.sql b/src/exchangedb/0002-kyc_attributes.sql
index 897aff561..66f3fc315 100644
--- a/src/exchangedb/0002-kyc_attributes.sql
+++ b/src/exchangedb/0002-kyc_attributes.sql
@@ -107,16 +107,6 @@ BEGIN
' ADD CONSTRAINT ' || table_name || '_serial_key '
'UNIQUE (kyc_attributes_serial_id)'
);
- -- The legitimization_serial is a foreign key.
- -- But, due to partitioning by h_payto, we can not simply reference
- -- the serial id of the legitimization_processes. Thus, the following
- -- is commented out.
- -- EXECUTE FORMAT (
- -- 'ALTER TABLE ' || table_name ||
- -- ' ADD CONSTRAINT ' || table_name || '_foreign_legitimization_processes'
- -- ' FOREIGN KEY (legitimization_serial) '
- -- ' REFERENCES legitimization_processes (legitimization_process_serial_id)' -- ON DELETE CASCADE
- -- );
-- To search similar users (e.g. during AML checks)
EXECUTE FORMAT (
'CREATE INDEX ' || table_name || '_similarity_index '
diff --git a/src/exchangedb/0004-refunds.sql b/src/exchangedb/0004-refunds.sql
new file mode 100644
index 000000000..eb9e7ad6e
--- /dev/null
+++ b/src/exchangedb/0004-refunds.sql
@@ -0,0 +1,35 @@
+
+CREATE FUNCTION constrain_table_refunds4 (
+ IN partition_suffix TEXT DEFAULT NULL
+)
+RETURNS void
+LANGUAGE plpgsql
+AS $$
+DECLARE
+ table_name TEXT DEFAULT 'refunds';
+BEGIN
+ table_name = concat_ws('_', table_name, partition_suffix);
+
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' DROP CONSTRAINT ' || table_name || '_pkey'
+ );
+ EXECUTE FORMAT (
+ 'ALTER TABLE ' || table_name ||
+ ' ADD PRIMARY KEY (batch_deposit_serial_id, coin_pub, rtransaction_id) '
+ );
+END
+$$;
+
+INSERT INTO exchange_tables
+ (name
+ ,version
+ ,action
+ ,partitioned
+ ,by_range)
+ VALUES
+ ('refunds4'
+ ,'exchange-0004'
+ ,'constrain'
+ ,TRUE
+ ,FALSE);
diff --git a/src/exchangedb/Makefile.am b/src/exchangedb/Makefile.am
index 45070ac08..fd993f968 100644
--- a/src/exchangedb/Makefile.am
+++ b/src/exchangedb/Makefile.am
@@ -19,8 +19,10 @@ sqlinputs = \
procedures.sql.in \
0002-*.sql \
0003-*.sql \
+ 0004-*.sql \
exchange-0002.sql.in \
- exchange-0003.sql.in
+ exchange-0003.sql.in \
+ exchange-0004.sql.in
sql_DATA = \
benchmark-0001.sql \
@@ -29,6 +31,7 @@ sql_DATA = \
exchange-0001.sql \
exchange-0002.sql \
exchange-0003.sql \
+ exchange-0004.sql \
drop.sql \
procedures.sql
@@ -44,20 +47,25 @@ CLEANFILES = \
procedures.sql
procedures.sql: procedures.sql.in exchange_do_*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < procedures.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
exchange-0002.sql: exchange-0002.sql.in 0002-*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < exchange-0002.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
exchange-0003.sql: exchange-0003.sql.in 0003-*.sql
- chmod +w $@ || true
+ chmod +w $@ 2> /dev/null || true
gcc -E -P -undef - < exchange-0003.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
chmod ugo-w $@
+exchange-0004.sql: exchange-0004.sql.in 0004-*.sql
+ chmod +w $@ 2> /dev/null || true
+ gcc -E -P -undef - < exchange-0004.sql.in 2>/dev/null | sed -e "s/--.*//" | awk 'NF' - >$@
+ chmod ugo-w $@
+
check_SCRIPTS = \
test_idempotency.sh
diff --git a/src/exchangedb/exchange-0004.sql.in b/src/exchangedb/exchange-0004.sql.in
new file mode 100644
index 000000000..c966aedc5
--- /dev/null
+++ b/src/exchangedb/exchange-0004.sql.in
@@ -0,0 +1,24 @@
+--
+-- This file is part of TALER
+-- Copyright (C) 2024 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/>
+--
+
+BEGIN;
+
+SELECT _v.register_patch('exchange-0004', NULL, NULL);
+SET search_path TO exchange;
+
+#include "0004-refunds.sql"
+
+COMMIT;
diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h
index e3ae829fd..b941316b5 100644
--- a/src/include/taler_crypto_lib.h
+++ b/src/include/taler_crypto_lib.h
@@ -1336,15 +1336,21 @@ struct TALER_TrackTransferDetails
struct TALER_CoinSpendPublicKeyP coin_pub;
/**
- * Value of the deposit (including fee).
+ * Value of the deposit (including fee), after refunds.
*/
struct TALER_Amount coin_value;
/**
- * Fee charged by the exchange for the deposit.
+ * Fee charged by the exchange for the deposit,
+ * possibly reduced (or waived) due to refunds.
*/
struct TALER_Amount coin_fee;
+ /**
+ * Total amount of refunds applied to this coin.
+ */
+ struct TALER_Amount refund_total;
+
};
diff --git a/src/include/taler_exchangedb_plugin.h b/src/include/taler_exchangedb_plugin.h
index 985664c43..2d5857677 100644
--- a/src/include/taler_exchangedb_plugin.h
+++ b/src/include/taler_exchangedb_plugin.h
@@ -4483,14 +4483,13 @@ struct TALER_EXCHANGEDB_Plugin
* @return query result status
*/
enum GNUNET_DB_QueryStatus
- (*select_refunds_by_coin)(void *cls,
- const struct TALER_CoinSpendPublicKeyP *coin_pub,
- const struct
- TALER_MerchantPublicKeyP *merchant_pub,
- const struct
- TALER_PrivateContractHashP *h_contract,
- TALER_EXCHANGEDB_RefundCoinCallback cb,
- void *cb_cls);
+ (*select_refunds_by_coin)(
+ void *cls,
+ const struct TALER_CoinSpendPublicKeyP *coin_pub,
+ const struct TALER_MerchantPublicKeyP *merchant_pub,
+ const struct TALER_PrivateContractHashP *h_contract,
+ TALER_EXCHANGEDB_RefundCoinCallback cb,
+ void *cb_cls);
/**
diff --git a/src/include/taler_mhd_lib.h b/src/include/taler_mhd_lib.h
index d93bc1e14..57d041758 100644
--- a/src/include/taler_mhd_lib.h
+++ b/src/include/taler_mhd_lib.h
@@ -173,8 +173,8 @@ TALER_MHD_reply_json_pack (struct MHD_Connection *connection,
* @return MHD result code
*/
#define TALER_MHD_REPLY_JSON_PACK(connection,response_code,...) \
- TALER_MHD_reply_json_steal (connection, GNUNET_JSON_PACK (__VA_ARGS__), \
- response_code)
+ TALER_MHD_reply_json_steal (connection, GNUNET_JSON_PACK (__VA_ARGS__), \
+ response_code)
/**
@@ -259,7 +259,7 @@ TALER_MHD_make_json_pack (const char *fmt,
* @return MHD response object
*/
#define TALER_MHD_MAKE_JSON_PACK(...) \
- TALER_MHD_make_json_steal (GNUNET_JSON_PACK (__VA_ARGS__))
+ TALER_MHD_make_json_steal (GNUNET_JSON_PACK (__VA_ARGS__))
/**
@@ -270,8 +270,8 @@ TALER_MHD_make_json_pack (const char *fmt,
* @return packer array entries (two!)
*/
#define TALER_MHD_PACK_EC(ec) \
- GNUNET_JSON_pack_uint64 ("code", ec), \
- GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec))
+ GNUNET_JSON_pack_uint64 ("code", ec), \
+ GNUNET_JSON_pack_string ("hint", TALER_ErrorCode_get_hint (ec))
/**
* Create a response indicating an internal error.
@@ -462,19 +462,19 @@ TALER_MHD_parse_request_arg_timeout (struct MHD_Connection *connection,
* the current time plus the value given under "timeout_ms" otherwise
*/
#define TALER_MHD_parse_request_timeout(connection,expiration) \
- do { \
- switch (TALER_MHD_parse_request_arg_timeout (connection, \
- expiration)) \
- { \
- case GNUNET_SYSERR: \
- GNUNET_break (0); \
- return MHD_NO; \
- case GNUNET_NO: \
- GNUNET_break_op (0); \
- case GNUNET_OK: \
- break; \
- } \
- } while (0)
+ do { \
+ switch (TALER_MHD_parse_request_arg_timeout (connection, \
+ expiration)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ case GNUNET_OK: \
+ break; \
+ } \
+ } while (0)
/**
@@ -505,20 +505,20 @@ TALER_MHD_parse_request_arg_number (struct MHD_Connection *connection,
* unchanged if value was not specified
*/
#define TALER_MHD_parse_request_number(connection,name,off) \
- do { \
- switch (TALER_MHD_parse_request_arg_number (connection, \
- name, \
- off)) \
- { \
- case GNUNET_SYSERR: \
- GNUNET_break (0); \
- return MHD_NO; \
- case GNUNET_NO: \
- GNUNET_break_op (0); \
- case GNUNET_OK: \
- break; \
- } \
- } while (0)
+ do { \
+ switch (TALER_MHD_parse_request_arg_number (connection, \
+ name, \
+ off)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ case GNUNET_OK: \
+ break; \
+ } \
+ } while (0)
/**
@@ -549,20 +549,20 @@ TALER_MHD_parse_request_arg_snumber (struct MHD_Connection *connection,
* unchanged if value was not specified
*/
#define TALER_MHD_parse_request_snumber(connection,name,val) \
- do { \
- switch (TALER_MHD_parse_request_arg_snumber (connection, \
- name, \
- val)) \
- { \
- case GNUNET_SYSERR: \
- GNUNET_break (0); \
- return MHD_NO; \
- case GNUNET_NO: \
- GNUNET_break_op (0); \
- case GNUNET_OK: \
- break; \
- } \
- } while (0)
+ do { \
+ switch (TALER_MHD_parse_request_arg_snumber (connection, \
+ name, \
+ val)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ case GNUNET_OK: \
+ break; \
+ } \
+ } while (0)
/**
@@ -593,20 +593,42 @@ TALER_MHD_parse_request_arg_amount (struct MHD_Connection *connection,
* unchanged if value was not specified
*/
#define TALER_MHD_parse_request_amount(connection,name,val) \
- do { \
- switch (TALER_MHD_parse_request_arg_amount (connection, \
- name, \
- val)) \
- { \
- case GNUNET_SYSERR: \
- GNUNET_break (0); \
- return MHD_NO; \
- case GNUNET_NO: \
- GNUNET_break_op (0); \
- case GNUNET_OK: \
- break; \
- } \
- } while (0)
+ do { \
+ switch (TALER_MHD_parse_request_arg_amount (connection, \
+ name, \
+ val)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ case GNUNET_OK: \
+ break; \
+ } \
+ } while (0)
+
+
+/**
+ * Determines which of the given choices is preferred
+ * by the client. Parses an HTTP header such as
+ * "Accept:" or "Language:" to find entries matching
+ * the list of strings given in the variadic argument
+ * list. Returns the index of the preferred choice.
+ *
+ * @param connection HTTP request handle
+ * @param header type of HTTP header to evaluate
+ * @param ... NULL-terminated list of choices to
+ * check for in the header
+ * @return -1 if none of the given choices is in
+ * the header, -2 if the header is missing,
+ * otherwise index of preferred choice in
+ * the varargs list
+ */
+int
+TALER_MHD_check_accept (struct MHD_Connection *connection,
+ const char *header,
+ ...);
/**
@@ -670,28 +692,28 @@ TALER_MHD_parse_request_header_data (struct MHD_Connection *connection,
* #GNUNET_SYSERR on internal error (error response could not be sent)
*/
#define TALER_MHD_parse_request_arg_auto(connection,name,val,required) \
- do { \
- bool p; \
- switch (TALER_MHD_parse_request_arg_data (connection, name, \
- val, sizeof (*val), &p)) \
- { \
- case GNUNET_SYSERR: \
- GNUNET_break (0); \
- return MHD_NO; \
- case GNUNET_NO: \
- GNUNET_break_op (0); \
- return MHD_YES; \
- case GNUNET_OK: \
- if (required & (! p)) \
- return TALER_MHD_reply_with_error ( \
- connection, \
- MHD_HTTP_BAD_REQUEST, \
- TALER_EC_GENERIC_PARAMETER_MISSING, \
- name); \
- required = p; \
- break; \
- } \
- } while (0)
+ do { \
+ bool p; \
+ switch (TALER_MHD_parse_request_arg_data (connection, name, \
+ val, sizeof (*val), &p)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ return MHD_YES; \
+ case GNUNET_OK: \
+ if (required & (! p)) \
+ return TALER_MHD_reply_with_error ( \
+ connection, \
+ MHD_HTTP_BAD_REQUEST, \
+ TALER_EC_GENERIC_PARAMETER_MISSING, \
+ name); \
+ required = p; \
+ break; \
+ } \
+ } while (0)
/**
@@ -706,10 +728,10 @@ TALER_MHD_parse_request_header_data (struct MHD_Connection *connection,
* #GNUNET_SYSERR on internal error (error response could not be sent)
*/
#define TALER_MHD_parse_request_arg_auto_t(connection,name,val) \
- do { \
- bool b = true; \
- TALER_MHD_parse_request_arg_auto (connection,name,val,b); \
- } while (0)
+ do { \
+ bool b = true; \
+ TALER_MHD_parse_request_arg_auto (connection,name,val,b); \
+ } while (0)
/**
* Extract fixed-size base32crockford encoded data from request.
@@ -725,28 +747,28 @@ TALER_MHD_parse_request_header_data (struct MHD_Connection *connection,
* #GNUNET_SYSERR on internal error (error response could not be sent)
*/
#define TALER_MHD_parse_request_header_auto(connection,name,val,required) \
- do { \
- bool p; \
- switch (TALER_MHD_parse_request_header_data (connection, name, \
- val, sizeof (*val), &p)) \
- { \
- case GNUNET_SYSERR: \
- GNUNET_break (0); \
- return MHD_NO; \
- case GNUNET_NO: \
- GNUNET_break_op (0); \
- return MHD_YES; \
- case GNUNET_OK: \
- if (required & (! p)) \
- return TALER_MHD_reply_with_error ( \
- connection, \
- MHD_HTTP_BAD_REQUEST, \
- TALER_EC_GENERIC_PARAMETER_MISSING, \
- name); \
- required = p; \
- break; \
- } \
- } while (0)
+ do { \
+ bool p; \
+ switch (TALER_MHD_parse_request_header_data (connection, name, \
+ val, sizeof (*val), &p)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ return MHD_YES; \
+ case GNUNET_OK: \
+ if (required & (! p)) \
+ return TALER_MHD_reply_with_error ( \
+ connection, \
+ MHD_HTTP_BAD_REQUEST, \
+ TALER_EC_GENERIC_PARAMETER_MISSING, \
+ name); \
+ required = p; \
+ break; \
+ } \
+ } while (0)
/**
@@ -761,10 +783,10 @@ TALER_MHD_parse_request_header_data (struct MHD_Connection *connection,
* #GNUNET_SYSERR on internal error (error response could not be sent)
*/
#define TALER_MHD_parse_request_header_auto_t(connection,name,val) \
- do { \
- bool b = true; \
- TALER_MHD_parse_request_header_auto (connection,name,val,b); \
- } while (0)
+ do { \
+ bool b = true; \
+ TALER_MHD_parse_request_header_auto (connection,name,val,b); \
+ } while (0)
/**
@@ -795,19 +817,19 @@ TALER_MHD_check_content_length_ (struct MHD_Connection *connection,
* @param max_len maximum allowed content length
*/
#define TALER_MHD_check_content_length(connection,max_len) \
- do { \
- switch (TALER_MHD_check_content_length_ (connection, max_len)) \
- { \
- case GNUNET_SYSERR: \
- GNUNET_break (0); \
- return MHD_NO; \
- case GNUNET_NO: \
- GNUNET_break_op (0); \
- return MHD_YES; \
- case GNUNET_OK: \
- break; \
- } \
- } while (0)
+ do { \
+ switch (TALER_MHD_check_content_length_ (connection, max_len)) \
+ { \
+ case GNUNET_SYSERR: \
+ GNUNET_break (0); \
+ return MHD_NO; \
+ case GNUNET_NO: \
+ GNUNET_break_op (0); \
+ return MHD_YES; \
+ case GNUNET_OK: \
+ break; \
+ } \
+ } while (0)
/**
diff --git a/src/include/taler_pq_lib.h b/src/include/taler_pq_lib.h
index 6fae8562a..f45de61d9 100644
--- a/src/include/taler_pq_lib.h
+++ b/src/include/taler_pq_lib.h
@@ -422,31 +422,6 @@ TALER_PQ_result_spec_array_amount (
struct TALER_Amount **amounts);
-/**
- * Blind sign public key expected.
- *
- * @param name name of the field in the table
- * @param[out] public_key where to store the denomination signature
- * @return array entry for the result specification to use
- */
-struct GNUNET_PQ_ResultSpec
-TALER_PQ_result_spec_blind_sign_pub (
- const char *name,
- struct GNUNET_CRYPTO_BlindSignPublicKey *public_key);
-
-
-/**
- * Blind sign private key expected.
- *
- * @param name name of the field in the table
- * @param[out] private_key where to store the denomination signature
- * @return array entry for the result specification to use
- */
-struct GNUNET_PQ_ResultSpec
-TALER_PQ_result_spec_blind_sign_priv (
- const char *name,
- struct GNUNET_CRYPTO_BlindSignPrivateKey *private_key);
-
#endif /* TALER_PQ_LIB_H_ */
/* end of include/taler_pq_lib.h */
diff --git a/src/lib/Makefile.am b/src/lib/Makefile.am
index 256efd11d..63dab7c80 100644
--- a/src/lib/Makefile.am
+++ b/src/lib/Makefile.am
@@ -18,7 +18,7 @@ lib_LTLIBRARIES = \
libtalerexchange.la
libtalerexchange_la_LDFLAGS = \
- -version-info 6:0:0 \
+ -version-info 7:0:0 \
-no-undefined
libtalerexchange_la_SOURCES = \
exchange_api_add_aml_decision.c \
@@ -87,8 +87,8 @@ libtalerexchange_la_LIBADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
+ -lcurl \
-lm \
- $(LIBGNURLCURL_LIBS) \
$(XLIB)
libtalerauditor_la_LDFLAGS = \
@@ -106,8 +106,8 @@ libtalerauditor_la_LIBADD = \
-lgnunetjson \
-lgnunetutil \
-ljansson \
+ -lcurl \
-lm \
- $(LIBGNURLCURL_LIBS) \
$(XLIB)
diff --git a/src/lib/exchange_api_handle.c b/src/lib/exchange_api_handle.c
index bce3f2876..fdadc8d2a 100644
--- a/src/lib/exchange_api_handle.c
+++ b/src/lib/exchange_api_handle.c
@@ -40,12 +40,12 @@
* Which version of the Taler protocol is implemented
* by this library? Used to determine compatibility.
*/
-#define EXCHANGE_PROTOCOL_CURRENT 17
+#define EXCHANGE_PROTOCOL_CURRENT 19
/**
* How many versions are we backwards compatible with?
*/
-#define EXCHANGE_PROTOCOL_AGE 0
+#define EXCHANGE_PROTOCOL_AGE 2
/**
* Set to 1 for extra debug logging.
diff --git a/src/lib/exchange_api_refund.c b/src/lib/exchange_api_refund.c
index 7401bfe4f..9159b55f2 100644
--- a/src/lib/exchange_api_refund.c
+++ b/src/lib/exchange_api_refund.c
@@ -323,6 +323,10 @@ handle_refund_finished (void *cls,
rr.hr.ec = TALER_JSON_get_error_code (j);
rr.hr.hint = TALER_JSON_get_error_hint (j);
break;
+ case MHD_HTTP_FAILED_DEPENDENCY:
+ rr.hr.ec = TALER_JSON_get_error_code (j);
+ rr.hr.hint = TALER_JSON_get_error_hint (j);
+ break;
case MHD_HTTP_PRECONDITION_FAILED:
if (GNUNET_OK !=
verify_failed_dependency_ok (rh,
diff --git a/src/lib/exchange_api_transfers_get.c b/src/lib/exchange_api_transfers_get.c
index 14cf51ff9..c558fb42e 100644
--- a/src/lib/exchange_api_transfers_get.c
+++ b/src/lib/exchange_api_transfers_get.c
@@ -153,21 +153,27 @@ check_transfers_get_response_ok (
GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
&detail->h_contract_terms),
GNUNET_JSON_spec_fixed_auto ("coin_pub", &detail->coin_pub),
- TALER_JSON_spec_amount_any ("deposit_value", &detail->coin_value),
- TALER_JSON_spec_amount_any ("deposit_fee", &detail->coin_fee),
+ TALER_JSON_spec_amount ("deposit_value",
+ total_expected.currency,
+ &detail->coin_value),
+ TALER_JSON_spec_amount ("deposit_fee",
+ total_expected.currency,
+ &detail->coin_fee),
+ GNUNET_JSON_spec_mark_optional (
+ TALER_JSON_spec_amount ("refund_total",
+ total_expected.currency,
+ &detail->refund_total),
+ NULL),
GNUNET_JSON_spec_end ()
};
+ GNUNET_assert (GNUNET_OK ==
+ TALER_amount_set_zero (td->total_amount.currency,
+ &detail->refund_total));
if ( (GNUNET_OK !=
GNUNET_JSON_parse (detail_j,
spec_detail,
NULL, NULL)) ||
- (GNUNET_OK !=
- TALER_amount_cmp_currency (&total_expected,
- &detail->coin_value)) ||
- (GNUNET_OK !=
- TALER_amount_cmp_currency (&total_expected,
- &detail->coin_fee)) ||
(0 >
TALER_amount_add (&total_expected,
&total_expected,
diff --git a/src/mhd/Makefile.am b/src/mhd/Makefile.am
index d36bba42f..cd3fe7701 100644
--- a/src/mhd/Makefile.am
+++ b/src/mhd/Makefile.am
@@ -16,7 +16,7 @@ libtalermhd_la_SOURCES = \
mhd_responses.c \
mhd_run.c
libtalermhd_la_LDFLAGS = \
- -version-info 2:0:2 \
+ -version-info 3:0:3 \
-no-undefined
libtalermhd_la_LIBADD = \
$(top_builddir)/src/json/libtalerjson.la \
diff --git a/src/mhd/mhd_legal.c b/src/mhd/mhd_legal.c
index 5a600953e..8353a6901 100644
--- a/src/mhd/mhd_legal.c
+++ b/src/mhd/mhd_legal.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2019, 2020, 2022 Taler Systems SA
+ Copyright (C) 2019, 2020, 2022, 2024 Taler Systems SA
TALER is free software; you can redistribute it and/or modify it under the
terms of the GNU Affero General Public License as published by the Free Software
@@ -131,6 +131,7 @@ mime_matches (const char *accept_pattern,
if ( (NULL == da) ||
(NULL == dm) )
return (0 == strcmp ("*", accept_pattern));
+ // FIXME: use TALER_MHD_check_accept() here!
/* FIXME: eventually, we might want to parse the "q=$FLOAT"
part after the ';' and figure out which one is the
best/preferred match instead of returning a boolean... */
@@ -187,6 +188,12 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING);
m = GNUNET_TIME_absolute_to_timestamp (a);
+ /* Round up to next full day to ensure the expiration
+ time does not become a fingerprint! */
+ a = GNUNET_TIME_absolute_round_down (a,
+ MAX_TERMS_CACHING);
+ a = GNUNET_TIME_absolute_add (a,
+ MAX_TERMS_CACHING);
TALER_MHD_get_date_string (m.abs_time,
dat);
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
@@ -351,7 +358,7 @@ TALER_MHD_reply_legal (struct MHD_Connection *conn,
GNUNET_break (MHD_YES ==
MHD_add_response_header (resp,
MHD_HTTP_HEADER_CACHE_CONTROL,
- "public max-age=864000"));
+ "public,max-age=864000"));
if (NULL != legal)
GNUNET_break (MHD_YES ==
MHD_add_response_header (resp,
diff --git a/src/mhd/mhd_parsing.c b/src/mhd/mhd_parsing.c
index 2c3312cf2..771319bd5 100644
--- a/src/mhd/mhd_parsing.c
+++ b/src/mhd/mhd_parsing.c
@@ -508,4 +508,66 @@ TALER_MHD_check_content_length_ (struct MHD_Connection *connection,
}
+int
+TALER_MHD_check_accept (struct MHD_Connection *connection,
+ const char *header,
+ ...)
+{
+ bool ret = false;
+ const char *accept;
+ char *a;
+ char *saveptr;
+
+ accept = MHD_lookup_connection_value (connection,
+ MHD_HEADER_KIND,
+ header);
+ if (NULL == accept)
+ return -2; /* no Accept header set */
+
+ a = GNUNET_strdup (accept);
+ for (char *t = strtok_r (a, ",", &saveptr);
+ NULL != t;
+ t = strtok_r (NULL, ",", &saveptr))
+ {
+ char *end;
+
+ /* skip leading whitespace */
+ while (isspace ((unsigned char) t[0]))
+ t++;
+ /* trim of ';q=' parameter and everything after space */
+ /* FIXME: eventually, we might want to parse the "q=$FLOAT"
+ part after the ';' and figure out which one is the
+ best/preferred match instead of returning a boolean... */
+ end = strchr (t, ';');
+ if (NULL != end)
+ *end = '\0';
+ end = strchr (t, ' ');
+ if (NULL != end)
+ *end = '\0';
+ {
+ va_list ap;
+ int off = 0;
+ const char *val;
+
+ va_start (ap,
+ header);
+ while (NULL != (val = va_arg (ap,
+ const char *)))
+ {
+ if (0 == strcasecmp (val,
+ t))
+ {
+ ret = off;
+ break;
+ }
+ off++;
+ }
+ va_end (ap);
+ }
+ }
+ GNUNET_free (a);
+ return ret;
+}
+
+
/* end of mhd_parsing.c */
diff --git a/src/pq/pq_query_helper.c b/src/pq/pq_query_helper.c
index d25b3223e..b1dfd4cf1 100644
--- a/src/pq/pq_query_helper.c
+++ b/src/pq/pq_query_helper.c
@@ -1336,204 +1336,4 @@ TALER_PQ_query_param_array_amount_with_currency (
}
-/**
- * Function called to convert input argument into SQL parameters.
- *
- * @param cls closure
- * @param data pointer to input argument
- * @param data_len number of bytes in @a data (if applicable)
- * @param[out] param_values SQL data to set
- * @param[out] param_lengths SQL length data to set
- * @param[out] param_formats SQL format data to set
- * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
- * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
- * @param scratch_length number of entries left in @a scratch
- * @return -1 on error, number of offsets used in @a scratch otherwise
- */
-static int
-qconv_blind_sign_pub (void *cls,
- const void *data,
- size_t data_len,
- void *param_values[],
- int param_lengths[],
- int param_formats[],
- unsigned int param_length,
- void *scratch[],
- unsigned int scratch_length)
-{
- const struct GNUNET_CRYPTO_BlindSignPublicKey *public_key = data;
- size_t tlen;
- size_t len;
- uint32_t be;
- char *buf;
- void *tbuf;
-
- (void) cls;
- (void) data_len;
- GNUNET_assert (1 == param_length);
- GNUNET_assert (scratch_length > 0);
- GNUNET_break (NULL == cls);
- be = htonl ((uint32_t) public_key->cipher);
- switch (public_key->cipher)
- {
- case GNUNET_CRYPTO_BSA_RSA:
- tlen = GNUNET_CRYPTO_rsa_public_key_encode (
- public_key->details.rsa_public_key,
- &tbuf);
- break;
- case GNUNET_CRYPTO_BSA_CS:
- tlen = sizeof (public_key->details.cs_public_key);
- break;
- default:
- GNUNET_assert (0);
- }
- len = tlen + sizeof (be);
- buf = GNUNET_malloc (len);
- GNUNET_memcpy (buf,
- &be,
- sizeof (be));
- switch (public_key->cipher)
- {
- case GNUNET_CRYPTO_BSA_RSA:
- GNUNET_memcpy (&buf[sizeof (be)],
- tbuf,
- tlen);
- GNUNET_free (tbuf);
- break;
- case GNUNET_CRYPTO_BSA_CS:
- GNUNET_memcpy (&buf[sizeof (be)],
- &public_key->details.cs_public_key,
- tlen);
- break;
- default:
- GNUNET_assert (0);
- }
-
- scratch[0] = buf;
- param_values[0] = (void *) buf;
- param_lengths[0] = len;
- param_formats[0] = 1;
- return 1;
-}
-
-
-/**
- * Generate query parameter for a blind sign public key of variable size.
- *
- * @param public_key pointer to the query parameter to pass
- */
-struct GNUNET_PQ_QueryParam
-TALER_PQ_query_param_blind_sign_pub (
- const struct GNUNET_CRYPTO_BlindSignPublicKey *public_key)
-{
- struct GNUNET_PQ_QueryParam res = {
- .conv = &qconv_blind_sign_pub,
- .data = public_key,
- .num_params = 1
- };
-
- return res;
-}
-
-
-/**
- * Function called to convert input argument into SQL parameters.
- *
- * @param cls closure
- * @param data pointer to input argument
- * @param data_len number of bytes in @a data (if applicable)
- * @param[out] param_values SQL data to set
- * @param[out] param_lengths SQL length data to set
- * @param[out] param_formats SQL format data to set
- * @param param_length number of entries available in the @a param_values, @a param_lengths and @a param_formats arrays
- * @param[out] scratch buffer for dynamic allocations (to be done via #GNUNET_malloc()
- * @param scratch_length number of entries left in @a scratch
- * @return -1 on error, number of offsets used in @a scratch otherwise
- */
-static int
-qconv_blind_sign_priv (void *cls,
- const void *data,
- size_t data_len,
- void *param_values[],
- int param_lengths[],
- int param_formats[],
- unsigned int param_length,
- void *scratch[],
- unsigned int scratch_length)
-{
- const struct GNUNET_CRYPTO_BlindSignPrivateKey *private_key = data;
- size_t tlen;
- size_t len;
- uint32_t be;
- char *buf;
- void *tbuf;
-
- (void) cls;
- (void) data_len;
- GNUNET_assert (1 == param_length);
- GNUNET_assert (scratch_length > 0);
- GNUNET_break (NULL == cls);
- be = htonl ((uint32_t) private_key->cipher);
- switch (private_key->cipher)
- {
- case GNUNET_CRYPTO_BSA_RSA:
- tlen = GNUNET_CRYPTO_rsa_private_key_encode (
- private_key->details.rsa_private_key,
- &tbuf);
- break;
- case GNUNET_CRYPTO_BSA_CS:
- tlen = sizeof (private_key->details.cs_private_key);
- break;
- default:
- GNUNET_assert (0);
- }
- len = tlen + sizeof (be);
- buf = GNUNET_malloc (len);
- GNUNET_memcpy (buf,
- &be,
- sizeof (be));
- switch (private_key->cipher)
- {
- case GNUNET_CRYPTO_BSA_RSA:
- GNUNET_memcpy (&buf[sizeof (be)],
- tbuf,
- tlen);
- GNUNET_free (tbuf);
- break;
- case GNUNET_CRYPTO_BSA_CS:
- GNUNET_memcpy (&buf[sizeof (be)],
- &private_key->details.cs_private_key,
- tlen);
- break;
- default:
- GNUNET_assert (0);
- }
-
- scratch[0] = buf;
- param_values[0] = (void *) buf;
- param_lengths[0] = len;
- param_formats[0] = 1;
- return 1;
-}
-
-
-/**
- * Generate query parameter for a blind sign private key of variable size.
- *
- * @param private_key pointer to the query parameter to pass
- */
-struct GNUNET_PQ_QueryParam
-TALER_PQ_query_param_blind_sign_priv (
- const struct GNUNET_CRYPTO_BlindSignPrivateKey *private_key)
-{
- struct GNUNET_PQ_QueryParam res = {
- .conv = &qconv_blind_sign_priv,
- .data = private_key,
- .num_params = 1
- };
-
- return res;
-}
-
-
/* end of pq/pq_query_helper.c */
diff --git a/src/pq/pq_result_helper.c b/src/pq/pq_result_helper.c
index e81c78302..384200cfb 100644
--- a/src/pq/pq_result_helper.c
+++ b/src/pq/pq_result_helper.c
@@ -1544,8 +1544,6 @@ TALER_PQ_result_spec_array_amount (
.cls = info,
};
return res;
-
-
}
@@ -1574,9 +1572,6 @@ TALER_PQ_result_spec_array_hash_code (
.cls = info,
};
return res;
-
-
}
-
/* end of pq_result_helper.c */
diff --git a/src/templating/.gitignore b/src/templating/.gitignore
index ac9c37f45..9ed2f3ff9 100644
--- a/src/templating/.gitignore
+++ b/src/templating/.gitignore
@@ -1,2 +1,3 @@
test_mustach_jansson
taler-mustach-tool
+mustach
diff --git a/src/templating/Makefile.am b/src/templating/Makefile.am
index c55f2964d..a79b109d1 100644
--- a/src/templating/Makefile.am
+++ b/src/templating/Makefile.am
@@ -62,10 +62,71 @@ test_mustach_jansson_LDADD = \
check_PROGRAMS = \
test_mustach_jansson
-check_SCRIPTS = \
- run-original-tests.sh
-
-TESTS = $(check_SCRIPTS) $(check_PROGRAMS)
+TESTS = $(check_PROGRAMS)
EXTRA_DIST = \
- $(check_SCRIPTS)
+ $(check_SCRIPTS) \
+ mustach-original-Makefile \
+ mustach.1.gz \
+ mustach.1.scd \
+ meson.build \
+ LICENSE.txt \
+ ORIGIN \
+ pkgcfgs \
+ README.md \
+ dotest.sh \
+ AUTHORS \
+ CHANGELOG.md \
+ mustach-json-c.h \
+ mustach-json-c.c \
+ mustach-cjson.h \
+ mustach-cjson.c \
+ test1/json \
+ test1/Makefile \
+ test1/must \
+ test1/resu.ref \
+ test1/vg.ref \
+ test2/json \
+ test2/Makefile \
+ test2/must \
+ test2/resu.ref \
+ test2/vg.ref \
+ test3/json \
+ test3/Makefile \
+ test3/must \
+ test3/resu.ref \
+ test3/vg.ref \
+ test4/json \
+ test4/Makefile \
+ test4/must \
+ test4/resu.ref \
+ test4/vg.ref \
+ test5/json \
+ test5/Makefile \
+ test5/must \
+ test5/must2 \
+ test5/must2.mustache \
+ test5/must3.mustache \
+ test5/resu.ref \
+ test5/vg.ref \
+ test6/json \
+ test6/Makefile \
+ test6/must \
+ test6/resu.ref \
+ test6/test-custom-write.c \
+ test6/vg.ref \
+ test7/base.mustache \
+ test7/json \
+ test7/Makefile \
+ test7/node.mustache \
+ test7/resu.ref \
+ test7/vg.ref \
+ test8/json \
+ test8/Makefile \
+ test8/must \
+ test8/resu.ref \
+ test8/vg.ref \
+ test-specs/test-specs.c \
+ test-specs/test-specs-cjson.ref \
+ test-specs/test-specs-jansson.ref \
+ test-specs/test-specs-json-c.ref
diff --git a/src/templating/dotest.sh b/src/templating/dotest.sh
index 99ff8b201..32f575c2e 100755
--- a/src/templating/dotest.sh
+++ b/src/templating/dotest.sh
@@ -1,20 +1,26 @@
#!/bin/sh
-mustach=${mustach:-../mustach}
-echo starting test
-if test "$NOVALGRIND" = 1
+# Exit, with error message (hard failure)
+exit_fail() {
+ echo " FAIL: " "$@" >&2
+ exit 1
+}
+
+mustach="${mustach:-../mustach}"
+echo "starting test"
+if ! valgrind --version 2> /dev/null
then
- $mustach "$@" > resu.last
+ $mustach "$@" > resu.last || exit_fail "ERROR! mustach command failed ($?)!"
else
- valgrind $mustach "$@" > resu.last 2> vg.last
+ valgrind $mustach "$@" > resu.last 2> vg.last || exit_fail "ERROR! valgrind + mustach command failed ($?)!"
sed -i 's:^==[0-9]*== ::' vg.last
- awk '/^ *total heap usage: .* allocs, .* frees,.*/{if($$4-$$6)exit(1)}' vg.last || echo "ERROR! Alloc/Free issue"
+ awk '/^ *total heap usage: .* allocs, .* frees,.*/{if($$4-$$6)exit(1)}' vg.last || exit_fail "ERROR! Alloc/Free issue"
fi
if diff -w resu.ref resu.last
then
echo "result ok"
else
- echo "ERROR! Result differs"
+ exit_fail "ERROR! Result differs"
fi
echo
-
+exit 0
diff --git a/src/templating/mustach-original-Makefile b/src/templating/mustach-original-Makefile
index c1ddbe63f..aee827583 100644
--- a/src/templating/mustach-original-Makefile
+++ b/src/templating/mustach-original-Makefile
@@ -237,14 +237,6 @@ uninstall:
rm -f $(DESTDIR)$(LIBDIR)/libmustach*.so*
rm -rf $(DESTDIR)$(INCLUDEDIR)/mustach
-# testing
-ifeq ($(valgrind),no)
- NOVALGRIND := 1
-else
- NOVALGRIND := $(shell which -s valgrind && echo 0 || echo 1)
-endif
-export NOVALGRIND
-
.PHONY: test test-basic test-specs
test: basic-tests spec-tests
@@ -311,4 +303,3 @@ manuals: mustach.1.gz
mustach.1.gz: mustach.1.scd
if which scdoc >/dev/null 2>&1; then scdoc < mustach.1.scd | gzip > mustach.1.gz; fi
-
diff --git a/src/templating/run-original-tests.sh b/src/templating/run-original-tests.sh
index cc666fd69..21481a286 100755
--- a/src/templating/run-original-tests.sh
+++ b/src/templating/run-original-tests.sh
@@ -1,18 +1,10 @@
#!/bin/bash
# This file is in the public domain.
-set -eu
+set -eux
export CFLAGS="-g"
-function build()
-{
- make clean
- make
-}
-
-# Install rebuild-on-exit handler (except for kill -9)
-trap build EXIT
-
+echo "Ensuring clean state on entry to upstream tests ..."
make clean
# The build fails if libjson-c-dev is not installed.
@@ -23,3 +15,5 @@ make -f mustach-original-Makefile mustach mustach-json-c.o || exit 77
make -f mustach-original-Makefile clean || true
make -f mustach-original-Makefile basic-tests
make -f mustach-original-Makefile clean || true
+
+exit 0
diff --git a/src/templating/templating_api.c b/src/templating/templating_api.c
index 3bda7de55..88a17c682 100644
--- a/src/templating/templating_api.c
+++ b/src/templating/templating_api.c
@@ -221,7 +221,7 @@ TALER_TEMPLATING_build (struct MHD_Connection *connection,
GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
"Failed to load template `%s'\n",
template);
- *http_status = MHD_HTTP_INTERNAL_SERVER_ERROR;
+ *http_status = MHD_HTTP_NOT_ACCEPTABLE;
*reply = TALER_MHD_make_error (TALER_EC_GENERIC_FAILED_TO_LOAD_TEMPLATE,
template);
return GNUNET_NO;
@@ -432,11 +432,12 @@ load_template (void *cls,
MHD_RESULT
-TALER_TEMPLATING_reply_error (struct MHD_Connection *connection,
- const char *template_basename,
- unsigned int http_status,
- enum TALER_ErrorCode ec,
- const char *detail)
+TALER_TEMPLATING_reply_error (
+ struct MHD_Connection *connection,
+ const char *template_basename,
+ unsigned int http_status,
+ enum TALER_ErrorCode ec,
+ const char *detail)
{
json_t *data;
enum GNUNET_GenericReturnValue ret;
diff --git a/src/templating/test7/Makefile b/src/templating/test7/Makefile
new file mode 100644
index 000000000..8e3a3b990
--- /dev/null
+++ b/src/templating/test7/Makefile
@@ -0,0 +1,8 @@
+.PHONY: test clean
+
+test:
+ @../dotest.sh json base.mustache
+
+clean:
+ rm -f resu.last vg.last
+
diff --git a/src/templating/test8/Makefile b/src/templating/test8/Makefile
new file mode 100644
index 000000000..1a3e57914
--- /dev/null
+++ b/src/templating/test8/Makefile
@@ -0,0 +1,8 @@
+.PHONY: test clean
+
+test:
+ @../dotest.sh json must
+
+clean:
+ rm -f resu.last vg.last
+
diff --git a/src/testing/taler-unified-setup.sh b/src/testing/taler-unified-setup.sh
index 2be6a97f1..d933cc819 100755
--- a/src/testing/taler-unified-setup.sh
+++ b/src/testing/taler-unified-setup.sh
@@ -29,6 +29,11 @@
set -eu
+# These break TALER_HOME control via TALER_TEST_HOME...
+unset XDG_DATA_HOME
+unset XDG_CONFIG_HOME
+unset XDG_CACHE_HOME
+
EXIT_STATUS=2
# Exit, with status code "skip" (no 'real' failure)
diff --git a/src/util/Makefile.am b/src/util/Makefile.am
index 2aea6b689..d2504588b 100644
--- a/src/util/Makefile.am
+++ b/src/util/Makefile.am
@@ -120,7 +120,7 @@ libtalerutil_la_LIBADD = \
-lm
libtalerutil_la_LDFLAGS = \
- -version-info 3:1:2 \
+ -version-info 3:3:2 \
-no-undefined
diff --git a/src/util/crypto_confirmation.c b/src/util/crypto_confirmation.c
index 204c373da..99552f150 100644
--- a/src/util/crypto_confirmation.c
+++ b/src/util/crypto_confirmation.c
@@ -261,6 +261,13 @@ TALER_build_pos_confirmation (const char *pos_key,
struct GNUNET_HashCode hkey;
struct TALER_AmountNBO ntotal;
+ if ( (NULL == total) ||
+ (GNUNET_YES !=
+ TALER_amount_is_valid (total) ) )
+ {
+ GNUNET_break_op (0);
+ return NULL;
+ }
TALER_amount_hton (&ntotal,
total);
GNUNET_assert (GNUNET_YES ==
diff --git a/src/util/payto.c b/src/util/payto.c
index 5e0391883..6092b73fd 100644
--- a/src/util/payto.c
+++ b/src/util/payto.c
@@ -1,6 +1,6 @@
/*
This file is part of TALER
- Copyright (C) 2019-2022 Taler Systems SA
+ Copyright (C) 2019-2024 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
@@ -101,7 +101,9 @@ TALER_payto_get_method (const char *payto_uri)
char *
TALER_xtalerbank_account_from_payto (const char *payto)
{
+ const char *host;
const char *beg;
+ const char *nxt;
const char *end;
if (0 != strncasecmp (payto,
@@ -111,23 +113,27 @@ TALER_xtalerbank_account_from_payto (const char *payto)
GNUNET_break_op (0);
return NULL;
}
- beg = strchr (&payto[strlen (PAYTO "x-taler-bank/")],
+ host = &payto[strlen (PAYTO "x-taler-bank/")];
+ beg = strchr (host,
'/');
if (NULL == beg)
{
GNUNET_break_op (0);
return NULL;
}
- beg++; /* now points to $ACCOUNT */
+ beg++; /* now points to $ACCOUNT or $PATH */
+ nxt = strchr (beg,
+ '/');
end = strchr (beg,
'?');
if (NULL == end)
+ end = &beg[strlen (beg)];
+ while ( (NULL != nxt) &&
+ (end - nxt > 0) )
{
- GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
- "Invalid payto URI `%s'\n",
- payto);
- GNUNET_break_op (0);
- return GNUNET_strdup (beg); /* optional part is missing */
+ beg = nxt + 1;
+ nxt = strchr (beg,
+ '/');
}
return GNUNET_strndup (beg,
end - beg);
@@ -200,36 +206,63 @@ static char *
validate_payto_xtalerbank (const char *account_url)
{
const char *user;
+ const char *nxt;
+ const char *beg;
+ const char *end;
const char *host;
bool dot_ok;
bool post_colon;
+ bool port_ok;
-#define XTALERBANK_PREFIX "payto://x-taler-bank/"
+#define XTALERBANK_PREFIX PAYTO "x-taler-bank/"
if (0 != strncasecmp (account_url,
XTALERBANK_PREFIX,
strlen (XTALERBANK_PREFIX)))
return NULL; /* not an IBAN */
host = &account_url[strlen (XTALERBANK_PREFIX)];
#undef XTALERBANK_PREFIX
- user = strchr (host, '/');
- if (NULL == user)
+ beg = strchr (host,
+ '/');
+ if (NULL == beg)
{
return GNUNET_strdup ("account name missing");
}
- if (user == host)
+ beg++; /* now points to $ACCOUNT or $PATH */
+ nxt = strchr (beg,
+ '/');
+ end = strchr (beg,
+ '?');
+ if (NULL == end)
+ {
+ return GNUNET_strdup ("'receiver-name' parameter missing");
+ }
+ while ( (NULL != nxt) &&
+ (end - nxt > 0) )
+ {
+ beg = nxt + 1;
+ nxt = strchr (beg,
+ '/');
+ }
+ user = beg;
+ if (user == host + 1)
{
return GNUNET_strdup ("domain name missing");
}
if ('-' == host[0])
return GNUNET_strdup ("invalid character '-' at start of domain name");
- if (NULL != strchr (user + 1, '/'))
- return GNUNET_strdup ("invalid character '/' after account name");
dot_ok = false;
post_colon = false;
+ port_ok = false;
while (host != user)
{
char c = host[0];
+ if ('/' == c)
+ {
+ /* path started, do not care about characters
+ in the path */
+ break;
+ }
if (':' == c)
{
post_colon = true;
@@ -247,6 +280,7 @@ validate_payto_xtalerbank (const char *account_url)
c);
return err;
}
+ port_ok = true;
}
else
{
@@ -275,6 +309,10 @@ validate_payto_xtalerbank (const char *account_url)
}
host++;
}
+ if (post_colon && (! port_ok) )
+ {
+ return GNUNET_strdup ("port missing after ':'");
+ }
{
char *target;
@@ -304,7 +342,7 @@ TALER_payto_validate (const char *payto_uri)
/* This is more strict than RFC 8905, alas we do not need to support messages/instructions/etc.,
and it is generally better to start with a narrow whitelist; we can be more permissive later ...*/
#define ALLOWED_CHARACTERS \
- "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:&?-.,=+%"
+ "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789/:&?-.,=+%~"
if (NULL == strchr (ALLOWED_CHARACTERS,
(int) payto_uri[i]))
{
@@ -358,10 +396,10 @@ TALER_payto_get_receiver_name (const char *payto)
/**
- * Normalize "payto://x-taler-bank/$HOSTNAME/$USERNAME"
+ * Normalize "payto://x-taler-bank/$HOSTNAME/[$PATH/]$USERNAME"
* URI in @a input.
*
- * Converts to lower-case, except for $USERNAME which
+ * Converts to lower-case, except for [$PATH/]$USERNAME which
* is case-sensitive.
*
* @param len number of bytes in @a input
diff --git a/src/util/test_payto.c b/src/util/test_payto.c
index b37a6f721..62ba7d28e 100644
--- a/src/util/test_payto.c
+++ b/src/util/test_payto.c
@@ -22,16 +22,16 @@
#include "taler_util.h"
#define CHECK(a,b) do { \
- GNUNET_assert (a != NULL); \
- GNUNET_assert (b != NULL); \
- if (0 != strcmp (a,b)) { \
- GNUNET_break (0); \
- fprintf (stderr, "Got %s, wanted %s\n", b, a); \
- GNUNET_free (b); \
- return 1; \
- } else { \
- GNUNET_free (b); \
- } \
+ GNUNET_assert (a != NULL); \
+ GNUNET_assert (b != NULL); \
+ if (0 != strcmp (a,b)) { \
+ GNUNET_break (0); \
+ fprintf (stderr, "Got %s, wanted %s\n", b, a); \
+ GNUNET_free (b); \
+ return 1; \
+ } else { \
+ GNUNET_free (b); \
+ } \
} while (0)
@@ -54,11 +54,20 @@ main (int argc,
"payto://x-taler-bank/hostname/username?receiver-name=foo");
GNUNET_assert (NULL == r);
r = TALER_payto_validate (
+ "payto://x-taler-bank/hostname/~path/username?receiver-name=foo");
+ GNUNET_assert (NULL == r);
+ r = TALER_payto_validate (
+ "payto://x-taler-bank/hostname/~path/username?receiver-name=fo/o");
+ GNUNET_assert (NULL == r);
+ r = TALER_payto_validate (
+ "payto://x-taler-bank/hostname/path/username?receiver-name=foo");
+ GNUNET_assert (NULL == r);
+ r = TALER_payto_validate (
"payto://x-taler-bank/https://hostname/username?receiver-name=foo");
GNUNET_assert (NULL != r);
GNUNET_free (r);
r = TALER_payto_validate (
- "payto://x-taler-bank/hostname/username/extra?receiver-name=foo");
+ "payto://x-taler-bank/hostname:4a2/path/username?receiver-name=foo");
GNUNET_assert (NULL != r);
GNUNET_free (r);
r = TALER_payto_validate (
@@ -82,6 +91,14 @@ main (int argc,
CHECK ("alice",
r);
r = TALER_xtalerbank_account_from_payto (
+ "payto://x-taler-bank/localhost:1080/path/alice");
+ CHECK ("alice",
+ r);
+ r = TALER_xtalerbank_account_from_payto (
+ "payto://x-taler-bank/localhost:1080/path/alice?receiver-name=ali/cia");
+ CHECK ("alice",
+ r);
+ r = TALER_xtalerbank_account_from_payto (
"payto://x-taler-bank/localhost:1080/alice?subject=hello&amount=EUR:1");
CHECK ("alice",
r);