From da75b30a258e35d6256aa8d61a0b729ae5c4c896 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Fri, 5 May 2017 11:11:03 +0200 Subject: [PATCH] starting to implement taler-exchange-wirewatch --- doc/Makefile.am | 1 + doc/taler-exchange-wirewatch.1 | 29 ++ src/exchange/.gitignore | 2 +- src/exchange/Makefile.am | 14 +- src/exchange/taler-exchange-aggregator.c | 8 +- src/exchange/taler-exchange-wirewatch.c | 372 +++++++++++++++++++++++ src/include/taler_wire_plugin.h | 13 +- src/wire/plugin_wire_sepa.c | 4 +- src/wire/plugin_wire_template.c | 4 +- src/wire/plugin_wire_test.c | 23 +- 10 files changed, 444 insertions(+), 26 deletions(-) create mode 100644 doc/taler-exchange-wirewatch.1 create mode 100644 src/exchange/taler-exchange-wirewatch.c diff --git a/doc/Makefile.am b/doc/Makefile.am index 7e9401a09..c509f4a8c 100644 --- a/doc/Makefile.am +++ b/doc/Makefile.am @@ -13,6 +13,7 @@ man_MANS = \ taler-exchange-keycheck.1 \ taler-exchange-reservemod.1 \ taler-exchange-wire.1 \ + taler-exchange-wirewatch.1 \ taler.conf.5 DISTCLEANFILES = \ diff --git a/doc/taler-exchange-wirewatch.1 b/doc/taler-exchange-wirewatch.1 new file mode 100644 index 000000000..72006e6d2 --- /dev/null +++ b/doc/taler-exchange-wirewatch.1 @@ -0,0 +1,29 @@ +.TH TALER\-EXCHANGE\-WIREWATCH 1 "May 5, 2017" "GNU Taler" + +.SH NAME +taler\-exchange\-wirewatch \- Watch for incoming wire transfers + +.SH SYNOPSIS +.B taler\-exchange\-wirewatch +.RI [ options ] +.br + +.SH DESCRIPTION +\fBtaler\-exchange\-wirewatch\fP is a command line tool to import wire transactions into the Taler exchange database. + +.SH OPTIONS +.B +.IP "\-t PLUGINNAME, \-\-type=PLUGINNAME" +Use the specified wire plugin and its configuration to talk to the bank. +.B +.IP "\-h, \-\-help" +Print short help on options. +.B +.IP "\-v, \-\-version" +Print version information. +.B +.SH BUGS +Report bugs by using Mantis or by sending electronic mail to + +.SH "SEE ALSO" +\fBtaler\-exchange\-aggregator\fP(1), \fBtaler\-exchange\-httpd\fP(1), \fBtaler.conf\fP(5) diff --git a/src/exchange/.gitignore b/src/exchange/.gitignore index a1e5e9aa7..f9dcae1fb 100644 --- a/src/exchange/.gitignore +++ b/src/exchange/.gitignore @@ -3,4 +3,4 @@ taler-exchange-keycheck taler-exchange-keyup taler-exchange-pursemod taler-exchange-reservemod -taler-exchange-httpd \ No newline at end of file +taler-exchange-httpdtaler-exchange-wirewatch diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am index f936c3a48..899f39684 100644 --- a/src/exchange/Makefile.am +++ b/src/exchange/Makefile.am @@ -13,7 +13,8 @@ pkgcfg_DATA = \ bin_PROGRAMS = \ taler-exchange-aggregator \ - taler-exchange-httpd + taler-exchange-httpd \ + taler-exchange-wirewatch dist_bin_SCRIPTS = \ taler-config-generate @@ -29,6 +30,17 @@ taler_exchange_aggregator_LDADD = \ -ljansson \ -lgnunetutil +taler_exchange_wirewatch_SOURCES = \ + taler-exchange-wirewatch.c +taler_exchange_wirewatch_LDADD = \ + $(LIBGCRYPT_LIBS) \ + $(top_builddir)/src/json/libtalerjson.la \ + $(top_builddir)/src/util/libtalerutil.la \ + $(top_builddir)/src/wire/libtalerwire.la \ + $(top_builddir)/src/exchangedb/libtalerexchangedb.la \ + -ljansson \ + -lgnunetutil + taler_exchange_httpd_SOURCES = \ taler-exchange-httpd.c taler-exchange-httpd.h \ taler-exchange-httpd_admin.c taler-exchange-httpd_admin.h \ diff --git a/src/exchange/taler-exchange-aggregator.c b/src/exchange/taler-exchange-aggregator.c index 54757d860..39e61f3ad 100644 --- a/src/exchange/taler-exchange-aggregator.c +++ b/src/exchange/taler-exchange-aggregator.c @@ -1583,7 +1583,7 @@ run (void *cls, /** - * The main function of the taler-exchange-httpd server ("the exchange"). + * The main function of the taler-exchange-aggregator. * * @param argc number of arguments from the command line * @param argv command line arguments @@ -1595,9 +1595,9 @@ main (int argc, { struct GNUNET_GETOPT_CommandLineOption options[] = { GNUNET_GETOPT_option_flag ('t', - "test", - "run in test mode and exit when idle", - &test_mode), + "test", + "run in test mode and exit when idle", + &test_mode), GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION), GNUNET_GETOPT_OPTION_END }; diff --git a/src/exchange/taler-exchange-wirewatch.c b/src/exchange/taler-exchange-wirewatch.c new file mode 100644 index 000000000..9c582dd56 --- /dev/null +++ b/src/exchange/taler-exchange-wirewatch.c @@ -0,0 +1,372 @@ +/* + This file is part of TALER + Copyright (C) 2016, 2017 GNUnet e.V. + + 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 + 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License along with + TALER; see the file COPYING. If not, see +*/ + +/** + * @file taler-exchange-wirewatch.c + * @brief Process that watches for wire transfers to the exchange's bank account + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include +#include "taler_exchangedb_lib.h" +#include "taler_exchangedb_plugin.h" +#include "taler_json_lib.h" +#include "taler_wire_lib.h" + +/** + * How long do we sleep before trying again if there + * are no transactions returned by the wire plugin? + */ +#define DELAY GNUNET_TIME_UNIT_SECONDS + + +/** + * Handle to the plugin. + */ +static struct TALER_WIRE_Plugin *wire_plugin; + +/** + * Which currency is used by this exchange? + */ +static char *exchange_currency_string; + +/** + * The exchange's configuration (global) + */ +static const struct GNUNET_CONFIGURATION_Handle *cfg; + +/** + * Our DB plugin. + */ +static struct TALER_EXCHANGEDB_Plugin *db_plugin; + +/** + * Value to return from main(). #GNUNET_OK on success, #GNUNET_SYSERR + * on serious errors. + */ +static int global_ret; + +/** + * Encoded offset in the wire transfer list that we + * processed last. + */ +static void *last_row_off; + +/** + * Number of bytes in #last_row_off. + */ +static size_t last_row_off_size; + +/** + * Encoded offset in the wire transfer list from where + * to start the next query with the bank. + */ +static void *start_off; + +/** + * Number of bytes in #start_off. + */ +static size_t start_off_size; + +/** + * Which wire plugin are we watching? + */ +static char *type; + +/** + * Should we delay the next request to the wire plugin a bit? + */ +static int delay; + +/** + * Next task to run, if any. + */ +static struct GNUNET_SCHEDULER_Task *task; + +/** + * Active request for history. + */ +static struct TALER_WIRE_HistoryHandle *hh; + + +/** + * We're being aborted with CTRL-C (or SIGTERM). Shut down. + * + * @param cls closure + */ +static void +shutdown_task (void *cls) +{ + if (NULL != task) + { + GNUNET_SCHEDULER_cancel (task); + task = NULL; + } + if (NULL != hh) + { + wire_plugin->get_history_cancel (wire_plugin->cls, + hh); + hh = NULL; + } + TALER_EXCHANGEDB_plugin_unload (db_plugin); + db_plugin = NULL; + TALER_WIRE_plugin_unload (wire_plugin); + wire_plugin = NULL; + GNUNET_free_non_null (start_off); + start_off = NULL; +} + + +/** + * Parse configuration parameters for the exchange server into the + * corresponding global variables. + * + * @return #GNUNET_OK on success + */ +static int +exchange_serve_process_config () +{ + if (GNUNET_OK != + GNUNET_CONFIGURATION_get_value_string (cfg, + "taler", + "currency", + &exchange_currency_string)) + { + GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, + "taler", + "currency"); + return GNUNET_SYSERR; + } + if (strlen (exchange_currency_string) >= TALER_CURRENCY_LEN) + { + fprintf (stderr, + "Currency `%s' longer than the allowed limit of %u characters.", + exchange_currency_string, + (unsigned int) TALER_CURRENCY_LEN); + return GNUNET_SYSERR; + } + + if (NULL == + (db_plugin = TALER_EXCHANGEDB_plugin_load (cfg))) + { + fprintf (stderr, + "Failed to initialize DB subsystem\n"); + return GNUNET_SYSERR; + } + + if (NULL == + (wire_plugin = TALER_WIRE_plugin_load (cfg, + type))) + { + fprintf (stderr, + "Failed to load wire plugin for `%s'\n", + type); + TALER_EXCHANGEDB_plugin_unload (db_plugin); + return GNUNET_SYSERR; + } + + return GNUNET_OK; +} + + +/** + * Query for incoming wire transfers. + * + * @param cls NULL + */ +static void +find_transfers (void *cls); + + +/** + * Callbacks of this type are used to serve the result of asking + * the bank for the transaction history. + * + * @param cls closure with the `struct TALER_EXCHANGEDB_Session *` + * @param dir direction of the transfer + * @param row_off identification of the position at which we are querying + * @param row_off_size number of bytes in @a row_off + * @param details details about the wire transfer + */ +static void +history_cb (void *cls, + enum TALER_BANK_Direction dir, + const void *row_off, + size_t row_off_size, + const struct TALER_BANK_TransferDetails *details) +{ + struct TALER_EXCHANGEDB_Session *session = cls; + + // TODO: store to DB... + if (TALER_BANK_DIRECTION_NONE == dir) + { + int ret; + + hh = NULL; + ret = db_plugin->commit (db_plugin->cls, + session); + if (GNUNET_OK == ret) + { + GNUNET_free_non_null (start_off); + start_off = last_row_off; + start_off_size = last_row_off_size; + } + if (GNUNET_YES == delay) + task = GNUNET_SCHEDULER_add_delayed (DELAY, + &find_transfers, + NULL); + else + task = GNUNET_SCHEDULER_add_now (&find_transfers, + NULL); + return; + } + + 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); +} + + +/** + * Query for incoming wire transfers. + * + * @param cls NULL + */ +static void +find_transfers (void *cls) +{ + struct TALER_EXCHANGEDB_Session *session; + + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Checking for incoming wire transfers\n"); + + if (NULL == (session = db_plugin->get_session (db_plugin->cls))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to obtain database session!\n"); + global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != + db_plugin->start (db_plugin->cls, + session)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start database transaction!\n"); + global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return; + } + delay = GNUNET_YES; + hh = wire_plugin->get_history (wire_plugin->cls, + TALER_BANK_DIRECTION_CREDIT, + start_off, + start_off_size, + 1024, + &history_cb, + session); + if (NULL == hh) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to start request for account history!\n"); + db_plugin->rollback (db_plugin->cls, + session); + global_ret = GNUNET_SYSERR; + GNUNET_SCHEDULER_shutdown (); + return; + } + /* FIXME: write last_off! */ +} + + +/** + * First task. + * + * @param cls closure, NULL + * @param args remaining command-line arguments + * @param cfgfile name of the configuration file used (for saving, can be NULL!) + * @param c configuration + */ +static void +run (void *cls, + char *const *args, + const char *cfgfile, + const struct GNUNET_CONFIGURATION_Handle *c) +{ + cfg = c; + if (GNUNET_OK != + exchange_serve_process_config ()) + { + global_ret = 1; + return; + } + + task = GNUNET_SCHEDULER_add_now (&find_transfers, + NULL); + GNUNET_SCHEDULER_add_shutdown (&shutdown_task, + cls); +} + + +/** + * The main function of taler-exchange-wirewatch + * + * @param argc number of arguments from the command line + * @param argv command line arguments + * @return 0 ok, 1 on error + */ +int +main (int argc, + char *const *argv) +{ + struct GNUNET_GETOPT_CommandLineOption options[] = { + GNUNET_GETOPT_option_string ('t', + "type", + "PLUGINNAME", + "which wire plugin to use", + &type), + GNUNET_GETOPT_option_version (VERSION "-" VCS_VERSION), + GNUNET_GETOPT_OPTION_END + }; + + if (GNUNET_OK != + GNUNET_STRINGS_get_utf8_args (argc, argv, + &argc, &argv)) + return 2; + if (GNUNET_OK != + GNUNET_PROGRAM_run (argc, argv, + "taler-exchange-wirewatch", + gettext_noop ("background process that watches for incomming wire transfers from customers"), + options, + &run, NULL)) + { + GNUNET_free ((void*) argv); + return 1; + } + GNUNET_free ((void*) argv); + return global_ret; +} + +/* end of taler-exchange-wirewatch.c */ diff --git a/src/include/taler_wire_plugin.h b/src/include/taler_wire_plugin.h index 5e3936804..032052ee5 100644 --- a/src/include/taler_wire_plugin.h +++ b/src/include/taler_wire_plugin.h @@ -46,25 +46,17 @@ typedef void * the bank for the transaction history. * * @param cls closure - * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request - * 0 if the bank's reply is bogus (fails to follow the protocol), - * #MHD_HTTP_NO_CONTENT if there are no more results; on success the - * last callback is always of this status (even if `abs(num_results)` were - * already returned). * @param dir direction of the transfer * @param row_off identification of the position at which we are querying * @param row_off_size number of bytes in @a row_off * @param details details about the wire transfer - * @param json detailed response from the HTTPD, or NULL if reply was not in JSON */ typedef void (*TALER_WIRE_HistoryResultCallback) (void *cls, - unsigned int http_status, enum TALER_BANK_Direction dir, const void *row_off, size_t row_off_size, - const struct TALER_BANK_TransferDetails *details, - const json_t *json); + const struct TALER_BANK_TransferDetails *details); /** @@ -283,7 +275,8 @@ struct TALER_WIRE_Plugin * @param whh operation to cancel */ void - (*get_history_cancel) (struct TALER_WIRE_HistoryHandle *whh); + (*get_history_cancel) (void *cls, + struct TALER_WIRE_HistoryHandle *whh); }; diff --git a/src/wire/plugin_wire_sepa.c b/src/wire/plugin_wire_sepa.c index 5baa3eaba..6300d8205 100644 --- a/src/wire/plugin_wire_sepa.c +++ b/src/wire/plugin_wire_sepa.c @@ -772,10 +772,12 @@ sepa_get_history (void *cls, /** * Cancel going over the account's history. * + * @param cls the @e cls of this struct with the plugin-specific state * @param whh operation to cancel */ static void -sepa_get_history_cancel (struct TALER_WIRE_HistoryHandle *whh) +sepa_get_history_cancel (void *cls, + struct TALER_WIRE_HistoryHandle *whh) { GNUNET_break (0); } diff --git a/src/wire/plugin_wire_template.c b/src/wire/plugin_wire_template.c index e94b2f05b..c4eefd194 100644 --- a/src/wire/plugin_wire_template.c +++ b/src/wire/plugin_wire_template.c @@ -256,10 +256,12 @@ template_get_history (void *cls, /** * Cancel going over the account's history. * + * @param cls the @e cls of this struct with the plugin-specific state * @param whh operation to cancel */ static void -template_get_history_cancel (struct TALER_WIRE_HistoryHandle *whh) +template_get_history_cancel (void *cls, + struct TALER_WIRE_HistoryHandle *whh) { GNUNET_break (0); } diff --git a/src/wire/plugin_wire_test.c b/src/wire/plugin_wire_test.c index b034fea80..604a36423 100644 --- a/src/wire/plugin_wire_test.c +++ b/src/wire/plugin_wire_test.c @@ -816,13 +816,18 @@ bhist_cb (void *cls, struct TALER_WIRE_HistoryHandle *whh = cls; uint64_t bserial_id = GNUNET_htonll (serial_id); - whh->hres_cb (whh->hres_cb_cls, - http_status, - dir, - &bserial_id, - sizeof (bserial_id), - details, - json); + if (MHD_HTTP_OK == http_status) + whh->hres_cb (whh->hres_cb_cls, + dir, + &bserial_id, + sizeof (bserial_id), + details); + else + whh->hres_cb (whh->hres_cb_cls, + TALER_BANK_DIRECTION_NONE, + NULL, + 0, + NULL); if (MHD_HTTP_OK != http_status) { whh->hh = NULL; @@ -917,10 +922,12 @@ test_get_history (void *cls, /** * Cancel going over the account's history. * + * @param cls the @e cls of this struct with the plugin-specific state * @param whh operation to cancel */ static void -test_get_history_cancel (struct TALER_WIRE_HistoryHandle *whh) +test_get_history_cancel (void *cls, + struct TALER_WIRE_HistoryHandle *whh) { TALER_BANK_history_cancel (whh->hh); GNUNET_free (whh);