aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2017-11-12 15:46:52 +0100
committerChristian Grothoff <christian@grothoff.org>2017-11-12 15:46:52 +0100
commit8440de13339a3b38d9efa18b69df409e45cde625 (patch)
treecd4c63c5d8c58eb9ac2dac93f057d8cb31f5ffca
parentf299130c50ca79efbfc8daf7f73aaba7ffb3a258 (diff)
work on #5077: reserve_pub vs. wtid issue, add reject functionality to wire plugin API (with stub implementations for now)
-rw-r--r--src/auditor/taler-wire-auditor.c80
-rw-r--r--src/exchange/taler-exchange-wirewatch.c110
-rw-r--r--src/include/taler_error_codes.h4
-rw-r--r--src/include/taler_wire_plugin.h71
-rw-r--r--src/wire/plugin_wire_sepa.c103
-rw-r--r--src/wire/plugin_wire_test.c202
-rw-r--r--src/wire/test_wire_plugin_transactions_test.c5
7 files changed, 503 insertions, 72 deletions
diff --git a/src/auditor/taler-wire-auditor.c b/src/auditor/taler-wire-auditor.c
index 4ee92566e..9c7399025 100644
--- a/src/auditor/taler-wire-auditor.c
+++ b/src/auditor/taler-wire-auditor.c
@@ -70,7 +70,8 @@ static struct GNUNET_CONTAINER_MultiHashMap *in_map;
/**
* Map with information about outgoing wire transfers.
- * Maps hashes of the wire offsets to `struct ReserveOutInfo`s.
+ * Maps hashes of the wire subjects (in binary encoding)
+ * to `struct ReserveOutInfo`s.
*/
static struct GNUNET_CONTAINER_MultiHashMap *out_map;
@@ -658,7 +659,9 @@ complain_out_not_found (void *cls,
"row", (json_int_t) 0,
"amount_wired", TALER_JSON_from_amount (&roi->details.amount),
"amount_justified", TALER_JSON_from_amount (&zero),
- "wtid", GNUNET_JSON_from_data_auto (&roi->details.reserve_pub), /* #5077 missnomer */
+ "wtid", (NULL == roi->details.wtid_s)
+ ? GNUNET_JSON_from_data_auto (&roi->details.wtid)
+ : json_string (roi->details.wtid_s),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (roi->details.execution_date),
"diagnostic", "justification for wire transfer not found"));
GNUNET_break (GNUNET_OK ==
@@ -726,6 +729,7 @@ history_debit_cb (void *cls,
const struct TALER_WIRE_TransferDetails *details)
{
struct ReserveOutInfo *roi;
+ struct GNUNET_HashCode rowh;
if (TALER_BANK_DIRECTION_NONE == dir)
{
@@ -735,13 +739,36 @@ history_debit_cb (void *cls,
check_exchange_wire_out ();
return GNUNET_OK;
}
+ if (NULL != details->wtid_s)
+ {
+ char *diagnostic;
+
+ GNUNET_CRYPTO_hash (row_off,
+ row_off_size,
+ &rowh);
+ GNUNET_asprintf (&diagnostic,
+ "malformed wire transfer subject `%s'",
+ details->wtid_s);
+ report (report_row_inconsistencies,
+ json_pack ("{s:s, s:I, s:o, s:o, s:s}",
+ "table", "bank wire log",
+ "row", (json_int_t) 0,
+ "amount", TALER_JSON_from_amount (&details->amount),
+ "wire_offset_hash", GNUNET_JSON_from_data_auto (&rowh),
+ "diagnostic", diagnostic));
+ /* TODO: report generator currently ignores 'amount' for this
+ table, maybe use a different table to report this issue! */
+ /* TODO: add 'amount' to some total amount that was badly wired! */
+ GNUNET_free (diagnostic);
+ return GNUNET_SYSERR;
+ }
roi = GNUNET_new (struct ReserveOutInfo);
- GNUNET_CRYPTO_hash (&details->reserve_pub, /* FIXME (#5077): missnomer */
- sizeof (details->reserve_pub),
+ GNUNET_CRYPTO_hash (&details->wtid,
+ sizeof (details->wtid),
&roi->subject_hash);
roi->details.amount = details->amount;
roi->details.execution_date = details->execution_date;
- roi->details.reserve_pub = details->reserve_pub; /* FIXME (#5077): missnomer & redundant */
+ roi->details.wtid = details->wtid;
roi->details.account_details = json_incref ((json_t *) details->account_details);
if (GNUNET_OK !=
GNUNET_CONTAINER_multihashmap_put (out_map,
@@ -749,13 +776,25 @@ history_debit_cb (void *cls,
roi,
GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY))
{
- GNUNET_break_op (0); /* duplicate wire offset is not allowed! */
+ char *diagnostic;
+
+ GNUNET_CRYPTO_hash (row_off,
+ row_off_size,
+ &rowh);
+ GNUNET_asprintf (&diagnostic,
+ "duplicate wire transfer subject `%s'",
+ TALER_B2S (&roi->subject_hash));
report (report_row_inconsistencies,
- json_pack ("{s:s, s:I, s:o, s:s}",
+ json_pack ("{s:s, s:I, s:o, s:o, s:s}",
"table", "bank wire log",
"row", (json_int_t) 0,
- "wire_offset_hash", GNUNET_JSON_from_data_auto (&roi->subject_hash),
- "diagnostic", "duplicate wire offset"));
+ "amount", TALER_JSON_from_amount (&details->amount),
+ "wire_offset_hash", GNUNET_JSON_from_data_auto (&rowh),
+ "diagnostic", diagnostic));
+ /* TODO: report generator currently ignores 'amount' for this
+ table, maybe use a different table to report this issue! */
+ /* TODO: add 'amount' to some total amount that was badly wired! */
+ GNUNET_free (diagnostic);
return GNUNET_SYSERR;
}
return GNUNET_OK;
@@ -830,7 +869,12 @@ reserve_in_cb (void *cls,
rii->row_off_size = wire_reference_size;
rii->details.amount = *credit;
rii->details.execution_date = execution_date;
- rii->details.reserve_pub = *reserve_pub;
+ /* reserve public key should be the WTID */
+ GNUNET_assert (sizeof (rii->details.wtid) ==
+ sizeof (*reserve_pub));
+ memcpy (&rii->details.wtid,
+ reserve_pub,
+ sizeof (*reserve_pub));
rii->details.account_details = json_incref ((json_t *) sender_account_details);
rii->rowid = rowid;
if (GNUNET_OK !=
@@ -875,7 +919,7 @@ complain_in_not_found (void *cls,
"row", (json_int_t) rii->rowid,
"amount_expected", TALER_JSON_from_amount (&rii->details.amount),
"amount_wired", TALER_JSON_from_amount (&zero),
- "wtid", GNUNET_JSON_from_data_auto (&rii->details.reserve_pub), /* also reserve_pub, but see #5077 missnomer */
+ "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date),
"diagnostic", "incoming wire transfer claimed by exchange not found"));
GNUNET_break (GNUNET_OK ==
@@ -966,16 +1010,16 @@ history_credit_cb (void *cls,
"diagnostic", "wire reference size missmatch"));
return GNUNET_OK;
}
- if (0 != memcmp (&details->reserve_pub,
- &rii->details.reserve_pub,
- sizeof (struct TALER_ReservePublicKeyP)))
+ if (0 != memcmp (&details->wtid,
+ &rii->details.wtid,
+ sizeof (struct TALER_WireTransferIdentifierRawP)))
{
report (report_reserve_in_inconsistencies,
json_pack ("{s:I, s:o, s:o, s:o, s:s, s:s}",
"row", GNUNET_JSON_from_data (row_off, row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount (&rii->details.amount),
"amount_wired", TALER_JSON_from_amount (&zero),
- "wtid", GNUNET_JSON_from_data_auto (&rii->details.reserve_pub), /* #5077 missnomer */
+ "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (rii->details.execution_date),
"diagnostic", "wire subject does not match"));
GNUNET_break (GNUNET_OK ==
@@ -987,7 +1031,7 @@ history_credit_cb (void *cls,
"row", GNUNET_JSON_from_data (row_off, row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount (&zero),
"amount_wired", TALER_JSON_from_amount (&details->amount),
- "wtid", GNUNET_JSON_from_data_auto (&details->reserve_pub), /* #5077 missnomer */
+ "wtid", GNUNET_JSON_from_data_auto (&details->wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
"diagnostic", "wire subject does not match"));
@@ -1005,7 +1049,7 @@ history_credit_cb (void *cls,
"row", GNUNET_JSON_from_data (row_off, row_off_size),
"amount_exchange_expected", TALER_JSON_from_amount (&rii->details.amount),
"amount_wired", TALER_JSON_from_amount (&details->amount),
- "wtid", GNUNET_JSON_from_data_auto (&details->reserve_pub), /* #5077 missnomer */
+ "wtid", GNUNET_JSON_from_data_auto (&details->wtid),
"timestamp", GNUNET_STRINGS_absolute_time_to_string (details->execution_date),
"diagnostic", "wire amount does not match"));
if (0 < TALER_amount_cmp (&details->amount,
@@ -1046,7 +1090,7 @@ history_credit_cb (void *cls,
json_pack ("{s:s, s:o, s:o}",
"amount", TALER_JSON_from_amount (&rii->details.amount),
"row", GNUNET_JSON_from_data (row_off, row_off_size),
- "wtid", GNUNET_JSON_from_data_auto (&rii->details.reserve_pub))); /* FIXME #5077 missnomer */
+ "wtid", GNUNET_JSON_from_data_auto (&rii->details.wtid)));
GNUNET_break (GNUNET_OK ==
TALER_amount_add (&total_missattribution_in,
&total_missattribution_in,
diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c
index 312f8ac5e..ca7f3bad3 100644
--- a/src/exchange/taler-exchange-wirewatch.c
+++ b/src/exchange/taler-exchange-wirewatch.c
@@ -37,6 +37,23 @@
/**
+ * Closure for #reject_cb().
+ */
+struct RejectContext
+{
+ /**
+ * Wire transfer subject that was illformed.
+ */
+ char *wtid_s;
+
+ /**
+ * Database session that encountered the problem.
+ */
+ struct TALER_EXCHANGEDB_Session *session;
+};
+
+
+/**
* Handle to the plugin.
*/
static struct TALER_WIRE_Plugin *wire_plugin;
@@ -109,6 +126,11 @@ static struct GNUNET_SCHEDULER_Task *task;
*/
static struct TALER_WIRE_HistoryHandle *hh;
+/**
+ * Active request to reject a wire transfer.
+ */
+static struct TALER_WIRE_RejectHandle *rt;
+
/**
* We're being aborted with CTRL-C (or SIGTERM). Shut down.
@@ -129,6 +151,15 @@ shutdown_task (void *cls)
hh);
hh = NULL;
}
+ if (NULL != rt)
+ {
+ char *wtid_s;
+
+ wtid_s = wire_plugin->reject_transfer_cancel (wire_plugin->cls,
+ rt);
+ rt = NULL;
+ GNUNET_free (wtid_s);
+ }
TALER_EXCHANGEDB_plugin_unload (db_plugin);
db_plugin = NULL;
TALER_WIRE_plugin_unload (wire_plugin);
@@ -205,6 +236,48 @@ find_transfers (void *cls);
/**
+ * Function called upon completion of the rejection of a wire transfer.
+ *
+ * @param cls closure with the `struct RejectContext`
+ * @param ec error code for the operation
+ */
+static void
+reject_cb (void *cls,
+ enum TALER_ErrorCode ec)
+{
+ struct RejectContext *rtc = cls;
+ enum GNUNET_DB_QueryStatus qs;
+
+ rt = NULL;
+ if (TALER_EC_NONE != ec)
+ {
+ fprintf (stderr,
+ "Failed to wire back transfer `%s': %d\n",
+ rtc->wtid_s,
+ ec);
+ GNUNET_free (rtc->wtid_s);
+ db_plugin->rollback (db_plugin->cls,
+ rtc->session);
+ GNUNET_free (rtc);
+ GNUNET_SCHEDULER_shutdown ();
+ return;
+ }
+ GNUNET_free (rtc->wtid_s);
+ qs = db_plugin->commit (db_plugin->cls,
+ rtc->session);
+ GNUNET_free (rtc);
+ if (0 <= qs)
+ {
+ GNUNET_free_non_null (start_off);
+ start_off = last_row_off;
+ start_off_size = last_row_off_size;
+ }
+ task = GNUNET_SCHEDULER_add_now (&find_transfers,
+ NULL);
+}
+
+
+/**
* Callbacks of this type are used to serve the result of asking
* the bank for the transaction history.
*
@@ -224,6 +297,7 @@ history_cb (void *cls,
{
struct TALER_EXCHANGEDB_Session *session = cls;
enum GNUNET_DB_QueryStatus qs;
+ struct TALER_ReservePublicKeyP reserve_pub;
if (TALER_BANK_DIRECTION_NONE == dir)
{
@@ -254,13 +328,45 @@ history_cb (void *cls,
NULL);
return GNUNET_OK; /* will be ignored anyway */
}
+ if (NULL != details->wtid_s)
+ {
+ struct RejectContext *rtc;
+
+ GNUNET_log (GNUNET_ERROR_TYPE_INFO,
+ "Wire transfer over %s has invalid subject `%s', sending it back!\n",
+ TALER_amount2s (&details->amount),
+ details->wtid_s);
+ if (last_row_off_size != row_off_size)
+ {
+ GNUNET_free_non_null (last_row_off);
+ last_row_off = GNUNET_malloc (row_off_size);
+ }
+ memcpy (last_row_off,
+ row_off,
+ row_off_size);
+ rtc = GNUNET_new (struct RejectContext);
+ rtc->session = session;
+ rtc->wtid_s = GNUNET_strdup (details->wtid_s);
+ rt = wire_plugin->reject_transfer (wire_plugin->cls,
+ row_off,
+ row_off_size,
+ &reject_cb,
+ rtc);
+ return GNUNET_SYSERR; /* will continue later... */
+ }
+
GNUNET_log (GNUNET_ERROR_TYPE_INFO,
"Adding wire transfer over %s with subject `%s'\n",
TALER_amount2s (&details->amount),
- TALER_B2S (&details->reserve_pub));
+ TALER_B2S (&details->wtid));
+ /* Wire transfer identifier == reserve public key */
+ GNUNET_assert (sizeof (reserve_pub) == sizeof (details->wtid));
+ memcpy (&reserve_pub,
+ &details->wtid,
+ sizeof (reserve_pub));
qs = db_plugin->reserves_in_insert (db_plugin->cls,
session,
- &details->reserve_pub,
+ &reserve_pub,
&details->amount,
details->execution_date,
details->account_details,
diff --git a/src/include/taler_error_codes.h b/src/include/taler_error_codes.h
index 65831f4a0..931b5ee1b 100644
--- a/src/include/taler_error_codes.h
+++ b/src/include/taler_error_codes.h
@@ -63,6 +63,10 @@ enum TALER_ErrorCode
*/
TALER_EC_INTERNAL_INVARIANT_FAILURE = 5,
+ /**
+ * Operation timed out.
+ */
+ TALER_EC_TIMEOUT = 6,
/* ********** generic error codes ************* */
diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h
index 969af3571..6e355baf6 100644
--- a/src/include/taler_wire_plugin.h
+++ b/src/include/taler_wire_plugin.h
@@ -59,13 +59,17 @@ struct TALER_WIRE_TransferDetails
struct GNUNET_TIME_Absolute execution_date;
/**
- * Reserve public key that was encoded in the wire transfer subject.
- * FIXME (#5077): this is incorrect for *outgoing* wire transfers.
- * Maybe use `struct TALER_WireTransferIdentifierRawP` here instead?
- * OTOH, we might want to make this even more generic in case of
- * invalid transfers, so that we can capture those as well!
+ * Binary data that was encoded in the wire transfer subject, if
+ * it decoded properly. Otherwise all-zeros and @e wtid_s is set.
*/
- struct TALER_ReservePublicKeyP reserve_pub;
+ struct TALER_WireTransferIdentifierRawP wtid;
+
+ /**
+ * Wire transfer identifer as a string. Set to NULL if the
+ * identifier was properly Base32 encoded and this @e wtid could be
+ * set instead.
+ */
+ char *wtid_s;
/**
* The other account that was involved
@@ -94,6 +98,18 @@ typedef int
/**
+ * Callbacks of this type are used to serve the result of asking
+ * the bank to reject a wire transfer.
+ *
+ * @param cls closure
+ * @param ec status of the operation, #TALER_EC_NONE on success
+ */
+typedef void
+(*TALER_WIRE_RejectTransferCallback) (void *cls,
+ enum TALER_ErrorCode ec);
+
+
+/**
* Handle returned for cancelling a preparation step.
*/
struct TALER_WIRE_PrepareHandle;
@@ -308,12 +324,55 @@ struct TALER_WIRE_Plugin
/**
* Cancel going over the account's history.
*
+ * @param cls plugins' closure
* @param whh operation to cancel
*/
void
(*get_history_cancel) (void *cls,
struct TALER_WIRE_HistoryHandle *whh);
+
+ /**
+ * Reject an incoming wire transfer that was obtained from the
+ * history. This function can be used to transfer funds back to
+ * the sender if the WTID was malformed (i.e. due to a typo).
+ *
+ * Calling `reject_transfer` twice on the same wire transfer should
+ * be idempotent, i.e. not cause the funds to be wired back twice.
+ * Furthermore, the transfer should henceforth be removed from the
+ * results returned by @e get_history.
+ *
+ * @param cls plugin's closure
+ * @param start_off offset of the wire transfer in plugin-specific format
+ * @param start_off_len number of bytes in @a start_off
+ * @param rej_cb function to call with the result of the operation
+ * @param rej_cb_cls closure for @a rej_cb
+ * @return handle to cancel the operation
+ */
+ struct TALER_WIRE_RejectHandle *
+ (*reject_transfer)(void *cls,
+ const void *start_off,
+ size_t start_off_len,
+ TALER_WIRE_RejectTransferCallback rej_cb,
+ void *rej_cb_cls);
+
+ /**
+ * Cancel ongoing reject operation. Note that the rejection may still
+ * proceed. Basically, if this function is called, the rejection may
+ * have happened or not. This function is usually used during shutdown
+ * or system upgrades. At a later point, the application must call
+ * @e reject_transfer again for this wire transfer, unless the
+ * @e get_history shows that the wire transfer no longer exists.
+ *
+ * @param cls plugins' closure
+ * @param rh operation to cancel
+ * @return closure of the callback of the operation
+ */
+ void *
+ (*reject_transfer_cancel)(void *cls,
+ struct TALER_WIRE_RejectHandle *rh);
+
+
};
diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c
index 5de3472b9..416acac7c 100644
--- a/src/wire/plugin_wire_sepa.c
+++ b/src/wire/plugin_wire_sepa.c
@@ -791,6 +791,107 @@ sepa_get_history_cancel (void *cls,
}
+
+/**
+ * Context for a rejection operation.
+ */
+struct TALER_WIRE_RejectHandle
+{
+ /**
+ * Function to call with the result.
+ */
+ TALER_WIRE_RejectTransferCallback rej_cb;
+
+ /**
+ * Closure for @e rej_cb.
+ */
+ void *rej_cb_cls;
+
+ /**
+ * Handle to task for timeout of operation.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
+};
+
+
+/**
+ * Rejection operation failed with timeout, notify callback
+ * and clean up.
+ *
+ * @param cls closure with `struct TALER_WIRE_RejectHandle`
+ */
+static void
+timeout_reject (void *cls)
+{
+ struct TALER_WIRE_RejectHandle *rh = cls;
+
+ rh->timeout_task = NULL;
+ rh->rej_cb (rh->rej_cb_cls,
+ TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
+ GNUNET_free (rh);
+}
+
+
+/**
+ * Reject an incoming wire transfer that was obtained from the
+ * history. This function can be used to transfer funds back to
+ * the sender if the WTID was malformed (i.e. due to a typo).
+ *
+ * Calling `reject_transfer` twice on the same wire transfer should
+ * be idempotent, i.e. not cause the funds to be wired back twice.
+ * Furthermore, the transfer should henceforth be removed from the
+ * results returned by @e get_history.
+ *
+ * @param cls plugin's closure
+ * @param start_off offset of the wire transfer in plugin-specific format
+ * @param start_off_len number of bytes in @a start_off
+ * @param rej_cb function to call with the result of the operation
+ * @param rej_cb_cls closure for @a rej_cb
+ * @return handle to cancel the operation
+ */
+static struct TALER_WIRE_RejectHandle *
+sepa_reject_transfer (void *cls,
+ const void *start_off,
+ size_t start_off_len,
+ TALER_WIRE_RejectTransferCallback rej_cb,
+ void *rej_cb_cls)
+{
+ struct TALER_WIRE_RejectHandle *rh;
+
+ GNUNET_break (0); /* not implemented, just a stub! */
+ rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
+ rh->rej_cb = rej_cb;
+ rh->rej_cb_cls = rej_cb_cls;
+ rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
+ rh);
+ return rh;
+}
+
+
+/**
+ * Cancel ongoing reject operation. Note that the rejection may still
+ * proceed. Basically, if this function is called, the rejection may
+ * have happened or not. This function is usually used during shutdown
+ * or system upgrades. At a later point, the application must call
+ * @e reject_transfer again for this wire transfer, unless the
+ * @e get_history shows that the wire transfer no longer exists.
+ *
+ * @param cls plugins' closure
+ * @param rh operation to cancel
+ * @return closure of the callback of the operation
+ */
+static void *
+sepa_reject_transfer_cancel (void *cls,
+ struct TALER_WIRE_RejectHandle *rh)
+{
+ void *ret = rh->rej_cb_cls;
+
+ GNUNET_SCHEDULER_cancel (rh->timeout_task);
+ GNUNET_free (rh);
+ return ret;
+}
+
+
/**
* Initialize sepa-wire subsystem.
*
@@ -832,6 +933,8 @@ libtaler_plugin_wire_sepa_init (void *cls)
plugin->execute_wire_transfer_cancel = &sepa_execute_wire_transfer_cancel;
plugin->get_history = &sepa_get_history;
plugin->get_history_cancel = &sepa_get_history_cancel;
+ plugin->reject_transfer = &sepa_reject_transfer;
+ plugin->reject_transfer_cancel = &sepa_reject_transfer_cancel;
return plugin;
}
diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c
index c41bd7e9f..e9d5ad085 100644
--- a/src/wire/plugin_wire_test.c
+++ b/src/wire/plugin_wire_test.c
@@ -820,52 +820,65 @@ bhist_cb (void *cls,
uint64_t bserial_id = GNUNET_htonll (serial_id);
struct TALER_WIRE_TransferDetails wd;
- if (MHD_HTTP_OK == http_status)
- {
- char *subject;
- char *space;
-
- wd.amount = details->amount;
- wd.execution_date = details->execution_date;
- subject = GNUNET_strdup (details->wire_transfer_subject);
- space = strchr (subject, (int) ' ');
- if (NULL != space)
- {
- /* Space separates the actual wire transfer subject from the
- exchange base URL (if present, expected only for outgoing
- transactions). So we cut the string off at the space. */
- *space = '\0';
- }
- /* NOTE: For a real bank, the subject should include a checksum! */
- if (GNUNET_OK !=
- GNUNET_STRINGS_string_to_data (subject,
- strlen (subject),
- &wd.reserve_pub,
- sizeof (wd.reserve_pub)))
+ switch (http_status) {
+ case MHD_HTTP_OK:
{
- GNUNET_break (0);
+ char *subject;
+ char *space;
+
+ wd.amount = details->amount;
+ wd.execution_date = details->execution_date;
+ subject = GNUNET_strdup (details->wire_transfer_subject);
+ space = strchr (subject, (int) ' ');
+ if (NULL != space)
+ {
+ /* Space separates the actual wire transfer subject from the
+ exchange base URL (if present, expected only for outgoing
+ transactions). So we cut the string off at the space. */
+ *space = '\0';
+ }
+ /* NOTE: For a real bank, the subject should include a checksum! */
+ if (GNUNET_OK !=
+ GNUNET_STRINGS_string_to_data (subject,
+ strlen (subject),
+ &wd.wtid,
+ sizeof (wd.wtid)))
+ {
+ /* Ill-formed wire subject, set binary version to all zeros
+ and pass as a string, this time including the part after
+ the space. */
+ memset (&wd.wtid,
+ 0,
+ sizeof (wd.wtid));
+ wd.wtid_s = details->wire_transfer_subject;
+ }
GNUNET_free (subject);
- /* NOTE: for a "real" bank, we would want to trigger logic to undo the
- wire transfer. However, for the "demo" bank, it should currently
- be "impossible" to do wire transfers with invalid subjects, and
- equally we thus don't need to undo them (and there is no API to do
- that nicely either right now). So we don't handle this case for now. */
- return;
+ wd.account_details = details->account_details;
+
+ if ( (NULL != whh->hres_cb) &&
+ (GNUNET_OK !=
+ whh->hres_cb (whh->hres_cb_cls,
+ dir,
+ &bserial_id,
+ sizeof (bserial_id),
+ &wd)) )
+ whh->hres_cb = NULL;
+ break;
}
- GNUNET_free (subject);
- wd.account_details = details->account_details;
-
- if ( (NULL != whh->hres_cb) &&
- (GNUNET_OK !=
- whh->hres_cb (whh->hres_cb_cls,
- dir,
- &bserial_id,
- sizeof (bserial_id),
- &wd)) )
- whh->hres_cb = NULL;
- }
- else
- {
+ case MHD_HTTP_NO_CONTENT:
+ if (NULL != whh->hres_cb)
+ (void) whh->hres_cb (whh->hres_cb_cls,
+ TALER_BANK_DIRECTION_NONE,
+ NULL,
+ 0,
+ NULL);
+ whh->hh = NULL;
+ GNUNET_free (whh);
+ break;
+ default:
+ /* FIXME: consider modifying API to pass more specific error code(s)
+ back to the application. */
+ GNUNET_break (0);
if (NULL != whh->hres_cb)
(void) whh->hres_cb (whh->hres_cb_cls,
TALER_BANK_DIRECTION_NONE,
@@ -874,6 +887,7 @@ bhist_cb (void *cls,
NULL);
whh->hh = NULL;
GNUNET_free (whh);
+ break;
}
}
@@ -976,6 +990,106 @@ test_get_history_cancel (void *cls,
/**
+ * Context for a rejection operation.
+ */
+struct TALER_WIRE_RejectHandle
+{
+ /**
+ * Function to call with the result.
+ */
+ TALER_WIRE_RejectTransferCallback rej_cb;
+
+ /**
+ * Closure for @e rej_cb.
+ */
+ void *rej_cb_cls;
+
+ /**
+ * Handle to task for timeout of operation.
+ */
+ struct GNUNET_SCHEDULER_Task *timeout_task;
+};
+
+
+/**
+ * Rejection operation failed with timeout, notify callback
+ * and clean up.
+ *
+ * @param cls closure with `struct TALER_WIRE_RejectHandle`
+ */
+static void
+timeout_reject (void *cls)
+{
+ struct TALER_WIRE_RejectHandle *rh = cls;
+
+ rh->timeout_task = NULL;
+ rh->rej_cb (rh->rej_cb_cls,
+ TALER_EC_NOT_IMPLEMENTED /* in the future: TALER_EC_TIMEOUT */);
+ GNUNET_free (rh);
+}
+
+
+/**
+ * Reject an incoming wire transfer that was obtained from the
+ * history. This function can be used to transfer funds back to
+ * the sender if the WTID was malformed (i.e. due to a typo).
+ *
+ * Calling `reject_transfer` twice on the same wire transfer should
+ * be idempotent, i.e. not cause the funds to be wired back twice.
+ * Furthermore, the transfer should henceforth be removed from the
+ * results returned by @e get_history.
+ *
+ * @param cls plugin's closure
+ * @param start_off offset of the wire transfer in plugin-specific format
+ * @param start_off_len number of bytes in @a start_off
+ * @param rej_cb function to call with the result of the operation
+ * @param rej_cb_cls closure for @a rej_cb
+ * @return handle to cancel the operation
+ */
+static struct TALER_WIRE_RejectHandle *
+test_reject_transfer (void *cls,
+ const void *start_off,
+ size_t start_off_len,
+ TALER_WIRE_RejectTransferCallback rej_cb,
+ void *rej_cb_cls)
+{
+ struct TALER_WIRE_RejectHandle *rh;
+
+ GNUNET_break (0); /* not implemented, just a stub! */
+ rh = GNUNET_new (struct TALER_WIRE_RejectHandle);
+ rh->rej_cb = rej_cb;
+ rh->rej_cb_cls = rej_cb_cls;
+ rh->timeout_task = GNUNET_SCHEDULER_add_now (&timeout_reject,
+ rh);
+ return rh;
+}
+
+
+/**
+ * Cancel ongoing reject operation. Note that the rejection may still
+ * proceed. Basically, if this function is called, the rejection may
+ * have happened or not. This function is usually used during shutdown
+ * or system upgrades. At a later point, the application must call
+ * @e reject_transfer again for this wire transfer, unless the
+ * @e get_history shows that the wire transfer no longer exists.
+ *
+ * @param cls plugins' closure
+ * @param rh operation to cancel
+ * @return closure of the callback of the operation
+ */
+static void *
+test_reject_transfer_cancel (void *cls,
+ struct TALER_WIRE_RejectHandle *rh)
+{
+ void *ret = rh->rej_cb_cls;
+
+ GNUNET_SCHEDULER_cancel (rh->timeout_task);
+ GNUNET_free (rh);
+ return ret;
+}
+
+
+/**
* Initialize test-wire subsystem.
*
* @param cls a configuration instance
@@ -1087,6 +1201,8 @@ libtaler_plugin_wire_test_init (void *cls)
plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel;
plugin->get_history = &test_get_history;
plugin->get_history_cancel = &test_get_history_cancel;
+ plugin->reject_transfer = &test_reject_transfer;
+ plugin->reject_transfer_cancel = &test_reject_transfer_cancel;
return plugin;
}
diff --git a/src/wire/test_wire_plugin_transactions_test.c b/src/wire/test_wire_plugin_transactions_test.c
index ce31e99ee..26331b5b1 100644
--- a/src/wire/test_wire_plugin_transactions_test.c
+++ b/src/wire/test_wire_plugin_transactions_test.c
@@ -211,9 +211,8 @@ history_result_cb (void *cls,
return GNUNET_SYSERR;
}
if (0 != memcmp (&wtid,
- &details->reserve_pub,
- GNUNET_MIN (sizeof (struct TALER_ReservePublicKeyP),
- sizeof (wtid))))
+ &details->wtid,
+ sizeof (struct TALER_WireTransferIdentifierRawP)))
{
GNUNET_break (0);
global_ret = GNUNET_SYSERR;