From 889595f986d922ffbcdcd746fdfad7f1a0e53595 Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 23 Apr 2023 17:45:53 +0200 Subject: make taler-merchant-wirewatch multi-instance capable --- src/backend/taler-merchant-wirewatch.c | 326 ++++++++++++++++----------- src/backenddb/Makefile.am | 3 +- src/backenddb/merchant-0005.sql | 19 +- src/backenddb/pg_insert_wirewatch_progress.c | 53 ----- src/backenddb/pg_insert_wirewatch_progress.h | 44 ---- src/backenddb/pg_lookup_instances.c | 31 ++- src/backenddb/pg_select_wirewatch_accounts.c | 147 ++++++++++++ src/backenddb/pg_select_wirewatch_accounts.h | 44 ++++ src/backenddb/pg_select_wirewatch_progress.c | 56 ----- src/backenddb/pg_select_wirewatch_progress.h | 44 ---- src/backenddb/pg_update_wirewatch_progress.c | 16 +- src/backenddb/pg_update_wirewatch_progress.h | 8 +- src/backenddb/plugin_merchantdb_postgres.c | 13 +- src/bank/mb_parse.c | 98 ++++++-- src/include/taler_merchant_bank_lib.h | 16 ++ src/include/taler_merchantdb_plugin.h | 67 ++++-- 16 files changed, 587 insertions(+), 398 deletions(-) delete mode 100644 src/backenddb/pg_insert_wirewatch_progress.c delete mode 100644 src/backenddb/pg_insert_wirewatch_progress.h create mode 100644 src/backenddb/pg_select_wirewatch_accounts.c create mode 100644 src/backenddb/pg_select_wirewatch_accounts.h delete mode 100644 src/backenddb/pg_select_wirewatch_progress.c delete mode 100644 src/backenddb/pg_select_wirewatch_progress.h (limited to 'src') diff --git a/src/backend/taler-merchant-wirewatch.c b/src/backend/taler-merchant-wirewatch.c index 7772befa..380bcc11 100644 --- a/src/backend/taler-merchant-wirewatch.c +++ b/src/backend/taler-merchant-wirewatch.c @@ -34,30 +34,83 @@ 30) /** - * The merchant's configuration. + * Information about a watch job. */ -static const struct GNUNET_CONFIGURATION_Handle *cfg; +struct Watch +{ + /** + * Kept in a DLL. + */ + struct Watch *next; + + /** + * Kept in a DLL. + */ + struct Watch *prev; + + /** + * Next task to run, if any. + */ + struct GNUNET_SCHEDULER_Task *task; + + /** + * For which instance are we importing bank transfers? + */ + char *instance_id; + + /** + * For which account are we importing bank transfers? + */ + char *payto_uri; + + /** + * Bank history request. + */ + struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh; + + /** + * Start row for the bank interaction. Exclusive. + */ + uint64_t start_row; + + /** + * Artificial delay to use between API calls. Used to + * throttle on failures. + */ + struct GNUNET_TIME_Relative delay; + + /** + * Login data for the bank. + */ + struct TALER_MERCHANT_BANK_AuthenticationData ad; + + /** + * Set to true if we found a transaction in the last iteration. + */ + bool found; + +}; + /** - * Our database plugin. + * Head of active watches. */ -static struct TALER_MERCHANTDB_Plugin *db_plugin; +static struct Watch *w_head; /** - * Login data for the bank. + * Tail of active watches. */ -static struct TALER_MERCHANT_BANK_AuthenticationData ad; +static struct Watch *w_tail; /** - * Next task to run, if any. + * The merchant's configuration. */ -static struct GNUNET_SCHEDULER_Task *task; +static const struct GNUNET_CONFIGURATION_Handle *cfg; /** - * Configuration section with authentication data. - * Set to default value, can be overridden via command-line. + * Our database plugin. */ -static char *section = "taler-merchant-wirewatch"; +static struct TALER_MERCHANTDB_Plugin *db_plugin; /** * Handle to the context for interacting with the bank. @@ -84,63 +137,60 @@ static unsigned int batch_size = 32; */ static int test_mode; -/** - * Bank history request. - */ -static struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh; - -/** - * Artificial delay to use between API calls. Used to - * throttle on failures. - */ -static struct GNUNET_TIME_Relative delay; - -/** - * For which instance are we importing bank transfers? - */ -static char *instance_id; - -/** - * Start row for the bank interaction. Exclusive. - */ -static uint64_t start_row; - -/** - * Set to true if we need to update instead of insert on the merchant_wirewatch table. - */ -static bool progress_update; - -/** - * Set to true if we found a transaction in the last iteration. - */ -static bool found; - /** * Save progress in DB. */ static void -save (void) +save (struct Watch *w) { enum GNUNET_DB_QueryStatus qs; - if (progress_update) - qs = db_plugin->update_wirewatch_progress (db_plugin->cls, - section, - start_row); - else - qs = db_plugin->insert_wirewatch_progress (db_plugin->cls, - section, - start_row); + qs = db_plugin->update_wirewatch_progress (db_plugin->cls, + w->instance_id, + w->payto_uri, + w->start_row); if (qs < 0) { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Failed to persist wirewatch progress (%d)\n", + "Failed to persist wirewatch progress for %s/%s (%d)\n", + w->instance_id, + w->payto_uri, qs); + GNUNET_SCHEDULER_shutdown (); + global_ret = EXIT_FAILURE; } } +/** + * Free resources of @a w. + * + * @param w watch job to terminate + */ +static void +end_watch (struct Watch *w) +{ + if (NULL != w->task) + { + GNUNET_SCHEDULER_cancel (w->task); + w->task = NULL; + } + if (NULL != w->hh) + { + TALER_MERCHANT_BANK_credit_history_cancel (w->hh); + w->hh = NULL; + } + GNUNET_free (w->instance_id); + GNUNET_free (w->payto_uri); + TALER_MERCHANT_BANK_auth_free (&w->ad); + GNUNET_CONTAINER_DLL_remove (w_head, + w_tail, + w); + GNUNET_free (w); +} + + /** * We're being aborted with CTRL-C (or SIGTERM). Shut down. * @@ -152,17 +202,13 @@ shutdown_task (void *cls) (void) cls; GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Running shutdown\n"); - if (NULL != task) + while (NULL != w_head) { - GNUNET_SCHEDULER_cancel (task); - task = NULL; - } - if (NULL != hh) - { - TALER_MERCHANT_BANK_credit_history_cancel (hh); - hh = NULL; + struct Watch *w = w_head; + + save (w); + end_watch (w); } - save (); TALER_MERCHANTDB_plugin_unload (db_plugin); db_plugin = NULL; cfg = NULL; @@ -176,7 +222,6 @@ shutdown_task (void *cls) GNUNET_CURL_gnunet_rc_destroy (rc); rc = NULL; } - TALER_MERCHANT_BANK_auth_free (&ad); } @@ -223,7 +268,7 @@ parse_subject (const char *subject, /** * Run next iteration. * - * @param cls NULL + * @param cls a `struct Watch *` */ static void do_work (void *cls); @@ -233,7 +278,7 @@ do_work (void *cls); * Callbacks of this type are used to serve the result of asking * the bank for the credit transaction history. * - * @param cls closure + * @param cls a `struct Watch *` * @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 @@ -252,13 +297,14 @@ credit_cb ( uint64_t serial_id, const struct TALER_MERCHANT_BANK_CreditDetails *details) { - (void) cls; + struct Watch *w = cls; + switch (http_status) { case 0: GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid HTTP response from bank\n"); - delay = GNUNET_TIME_STD_BACKOFF (delay); + w->delay = GNUNET_TIME_STD_BACKOFF (w->delay); break; case MHD_HTTP_OK: { @@ -270,7 +316,7 @@ credit_cb ( "Received wire transfer `%s' over %s\n", details->wire_subject, TALER_amount2s (&details->amount)); - found = true; + w->found = true; if (GNUNET_OK != parse_subject (details->wire_subject, &wtid, @@ -280,11 +326,11 @@ credit_cb ( "Skipping transfer %llu (%s): not from exchange\n", (unsigned long long) serial_id, details->wire_subject); - start_row = serial_id; + w->start_row = serial_id; return GNUNET_OK; } qs = db_plugin->insert_transfer (db_plugin->cls, - instance_id, + w->instance_id, exchange_url, &wtid, &details->amount, @@ -294,7 +340,7 @@ credit_cb ( { GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Inserting transfer for %s into database failed. Is the credit account %s configured correctly?\n", - instance_id, + w->instance_id, details->credit_account_uri); } GNUNET_free (exchange_url); @@ -305,31 +351,33 @@ credit_cb ( return GNUNET_SYSERR; } } - start_row = serial_id; + w->start_row = serial_id; return GNUNET_OK; case MHD_HTTP_NO_CONTENT: - save (); - delay = GNUNET_TIME_UNIT_ZERO; + save (w); + w->delay = GNUNET_TIME_UNIT_ZERO; break; default: - delay = GNUNET_TIME_STD_BACKOFF (delay); + w->delay = GNUNET_TIME_STD_BACKOFF (w->delay); GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Unexpected HTTP status code %u(%d) from bank\n", http_status, ec); break; } - hh = NULL; - if (test_mode && (! found)) + w->hh = NULL; + if (test_mode && (! w->found)) { GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "No transactions found and in test mode. Shutting down!\n"); - GNUNET_SCHEDULER_shutdown (); + "No transactions found and in test mode. Ending watch!\n"); + end_watch (w); + if (NULL == w_head) + GNUNET_SCHEDULER_shutdown (); return GNUNET_OK; } - task = GNUNET_SCHEDULER_add_delayed (delay, - &do_work, - NULL); + w->task = GNUNET_SCHEDULER_add_delayed (w->delay, + &do_work, + w); return GNUNET_OK; } @@ -337,19 +385,20 @@ credit_cb ( static void do_work (void *cls) { - (void) cls; - task = NULL; - found = false; - hh = TALER_MERCHANT_BANK_credit_history (ctx, - &ad, - start_row, - batch_size, - test_mode - ? GNUNET_TIME_UNIT_ZERO - : BANK_TIMEOUT, - &credit_cb, - NULL); - if (NULL == hh) + struct Watch *w = cls; + + w->task = NULL; + w->found = false; + w->hh = TALER_MERCHANT_BANK_credit_history (ctx, + &w->ad, + w->start_row, + batch_size, + test_mode + ? GNUNET_TIME_UNIT_ZERO + : BANK_TIMEOUT, + &credit_cb, + w); + if (NULL == w->hh) { GNUNET_break (0); GNUNET_SCHEDULER_shutdown (); @@ -358,6 +407,55 @@ do_work (void *cls) } +/** + * Function called with information about a accounts + * the wirewatcher should monitor. + * + * @param cls closure (NULL) + * @param instance instance that owns the account + * @param payto_uri account URI + * @param credit_facade_url URL for the credit facade + * @param credit_facade_credentials account access credentials + * @param last_serial last transaction serial (inclusive) we have seen from this account + */ +static void +start_watch ( + void *cls, + const char *instance, + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + uint64_t last_serial) +{ + struct Watch *w = GNUNET_new (struct Watch); + + (void) cls; + if (GNUNET_OK != + TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials, + credit_facade_url, + &w->ad)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to parse authentication data of `%s/%s'\n", + instance, + payto_uri); + GNUNET_free (w); + GNUNET_SCHEDULER_shutdown (); + global_ret = 1; + return; + } + + GNUNET_CONTAINER_DLL_insert (w_head, + w_tail, + w); + w->instance_id = GNUNET_strdup (instance); + w->payto_uri = GNUNET_strdup (payto_uri); + w->start_row = last_serial; + w->task = GNUNET_SCHEDULER_add_now (&do_work, + w); +} + + /** * First task. * @@ -376,28 +474,6 @@ run (void *cls, (void) cfgfile; cfg = c; - if (GNUNET_OK != - TALER_MERCHANT_BANK_auth_parse_cfg (cfg, - section, - &ad)) - { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to parse authentication data in `%s'\n", - section); - return; - } - if (GNUNET_OK != - GNUNET_CONFIGURATION_get_value_string (cfg, - section, - "INSTANCE", - &instance_id)) - { - GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, - section, - "INSTANCE"); - TALER_MERCHANT_BANK_auth_free (&ad); - return; - } GNUNET_SCHEDULER_add_shutdown (&shutdown_task, NULL); ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, @@ -425,24 +501,21 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } + // FIXME: also add notification job! { enum GNUNET_DB_QueryStatus qs; - qs = db_plugin->select_wirewatch_progress (db_plugin->cls, - section, - &start_row); + qs = db_plugin->select_wirewatch_accounts (db_plugin->cls, + &start_watch, + NULL); if (qs < 0) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Failed to obtain wirewatch progress from database\n"); + "Failed to obtain wirewatch accounts from database\n"); GNUNET_SCHEDULER_shutdown (); return; } - progress_update = (0 != qs); } - GNUNET_assert (NULL == task); - task = GNUNET_SCHEDULER_add_now (&do_work, - NULL); } @@ -458,11 +531,6 @@ main (int argc, char *const *argv) { struct GNUNET_GETOPT_CommandLineOption options[] = { - GNUNET_GETOPT_option_string ('s', - "section", - "SECTION", - "configuration section to use for bank authentication data", - §ion), GNUNET_GETOPT_option_flag ('t', "test", "run in test mode and exit when idle", diff --git a/src/backenddb/Makefile.am b/src/backenddb/Makefile.am index 01947a81..189b7a32 100644 --- a/src/backenddb/Makefile.am +++ b/src/backenddb/Makefile.am @@ -52,9 +52,8 @@ libtalermerchantdb_la_LDFLAGS = \ -no-undefined libtaler_plugin_merchantdb_postgres_la_SOURCES = \ - pg_insert_wirewatch_progress.h pg_insert_wirewatch_progress.c \ pg_update_wirewatch_progress.h pg_update_wirewatch_progress.c \ - pg_select_wirewatch_progress.h pg_select_wirewatch_progress.c \ + pg_select_wirewatch_accounts.h pg_select_wirewatch_accounts.c \ pg_lookup_instances.h pg_lookup_instances.c \ pg_lookup_transfers.h pg_lookup_transfers.c \ plugin_merchantdb_postgres.c pg_helper.h diff --git a/src/backenddb/merchant-0005.sql b/src/backenddb/merchant-0005.sql index f558cbd2..1f3b9131 100644 --- a/src/backenddb/merchant-0005.sql +++ b/src/backenddb/merchant-0005.sql @@ -29,15 +29,16 @@ COMMENT ON COLUMN merchant_instances.user_type IS 'what type of user is this (individual or business)'; -CREATE TABLE IF NOT EXISTS merchant_wirewatch - (account_section VARCHAR PRIMARY KEY - ,last_bank_serial INT8 NOT NULL - ); -COMMENT ON TABLE merchant_wirewatch - IS 'table used to keep track of progress made by the taler-merchant-wirewatch tool'; -COMMENT ON COLUMN merchant_wirewatch.account_section - IS 'Name of the configuration section that specifies the bank account details and merchant instance being tracked here'; -COMMENT ON COLUMN merchant_wirewatch.last_bank_serial +ALTER TABLE merchant_accounts + ADD COLUMN credit_facade_url VARCHAR, + ADD COLUMN credit_facade_credentials VARCHAR, + ADD COLUMN last_bank_serial INT8 NOT NULL DEFAULT (0); + +COMMENT ON COLUMN merchant_accounts.credit_facade_url + IS 'Base URL of a facade where the merchant can inquire about incoming bank transactions into this account'; +COMMENT ON COLUMN merchant_accounts.credit_facade_credentials + IS 'JSON with credentials needed to access the credit facade'; +COMMENT ON COLUMN merchant_accounts.last_bank_serial IS 'Serial number of the bank of the last transaction we successfully imported'; -- Complete transaction diff --git a/src/backenddb/pg_insert_wirewatch_progress.c b/src/backenddb/pg_insert_wirewatch_progress.c deleted file mode 100644 index f4d1b0f8..00000000 --- a/src/backenddb/pg_insert_wirewatch_progress.c +++ /dev/null @@ -1,53 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 - */ -/** - * @file backenddb/pg_insert_wirewatch_progress.c - * @brief Implementation of the insert_wirewatch_progress function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include -#include "pg_insert_wirewatch_progress.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -TMH_PG_insert_wirewatch_progress ( - void *cls, - const char *section, - uint64_t last_serial) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (section), - GNUNET_PQ_query_param_uint64 (&last_serial), - GNUNET_PQ_query_param_end - }; - - PREPARE (pg, - "insert_wirewatch_progress", - "INSERT INTO merchant_wirewatch" - " (account_section" - " ,last_bank_serial)" - " VALUES" - " ($1,$2);"); - check_connection (pg); - return GNUNET_PQ_eval_prepared_non_select (pg->conn, - "insert_wirewatch_progress", - params); -} diff --git a/src/backenddb/pg_insert_wirewatch_progress.h b/src/backenddb/pg_insert_wirewatch_progress.h deleted file mode 100644 index 4af11b9e..00000000 --- a/src/backenddb/pg_insert_wirewatch_progress.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 - */ -/** - * @file backenddb/pg_insert_wirewatch_progress.h - * @brief implementation of the insert_wirewatch_progress function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_INSERT_WIREWATCH_PROGRESS_H -#define PG_INSERT_WIREWATCH_PROGRESS_H - -#include -#include -#include "taler_merchantdb_plugin.h" - - -/** - * Insert information about progress made by taler-merchant-wirewatch. - * - * @param cls closure - * @param section configuration section of the taler-merchant-wirewatch - * @param last_serial last serial imported from the bank - * @return transaction status - */ -enum GNUNET_DB_QueryStatus -TMH_PG_insert_wirewatch_progress ( - void *cls, - const char *section, - uint64_t last_serial); - - -#endif diff --git a/src/backenddb/pg_lookup_instances.c b/src/backenddb/pg_lookup_instances.c index acf3ada3..d876ad69 100644 --- a/src/backenddb/pg_lookup_instances.c +++ b/src/backenddb/pg_lookup_instances.c @@ -1,6 +1,6 @@ /* This file is part of TALER - Copyright (C) 2022 Taler Systems SA + Copyright (C) 2022, 2023 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 @@ -98,6 +98,8 @@ prepare (struct PostgresClosure *pg) " h_wire" ",salt" ",payto_uri" + ",credit_facade_url" + ",credit_facade_credentials" ",active" " FROM merchant_accounts" " WHERE merchant_serial=$1"); @@ -165,7 +167,7 @@ call_with_accounts (struct LookupInstancesContext *lic, * Function to be called with the results of a SELECT statement * that has returned @a num_results results about accounts. * - * @param cls of type `struct FindInstancesContext *` + * @param cls of type `struct LookupInstancesContext *` * @param result the postgres result * @param num_results the number of results in @a result */ @@ -176,8 +178,16 @@ lookup_accounts_cb (void *cls, { struct LookupInstancesContext *lic = cls; char *paytos[num_results]; + char *facade_urls[num_results]; + json_t *credentials[num_results]; struct TALER_MERCHANTDB_AccountDetails accounts[num_results]; + memset (facade_urls, + 0, + sizeof (facade_urls)); + memset (credentials, + 0, + sizeof (credentials)); /* Note: this memset is completely superfluous, but gcc-11 (and gcc-12) have a bug creating a warning without it! See #7585 */ memset (accounts, @@ -193,6 +203,14 @@ lookup_accounts_cb (void *cls, &account->salt), GNUNET_PQ_result_spec_string ("payto_uri", &paytos[i]), + GNUNET_PQ_result_spec_allow_null ( + GNUNET_PQ_result_spec_string ("credit_facade_url", + &facade_urls[i]), + NULL), + GNUNET_PQ_result_spec_allow_null ( + TALER_PQ_result_spec_json ("credit_facade_credentials", + &credentials[i]), + NULL), GNUNET_PQ_result_spec_bool ("active", &account->active), GNUNET_PQ_result_spec_end @@ -206,7 +224,11 @@ lookup_accounts_cb (void *cls, GNUNET_break (0); lic->qs = GNUNET_DB_STATUS_HARD_ERROR; for (unsigned int j = 0; j < i; j++) + { GNUNET_free (paytos[j]); + GNUNET_free (facade_urls[j]); + json_decref (credentials[j]); + } return; } account->payto_uri = paytos[i]; @@ -215,7 +237,11 @@ lookup_accounts_cb (void *cls, num_results, accounts); for (unsigned int i = 0; i < num_results; i++) + { GNUNET_free (paytos[i]); + GNUNET_free (facade_urls[i]); + json_decref (credentials[i]); + } } @@ -442,4 +468,3 @@ TMH_PG_lookup_instance (void *cls, return lic.qs; return qs; } - diff --git a/src/backenddb/pg_select_wirewatch_accounts.c b/src/backenddb/pg_select_wirewatch_accounts.c new file mode 100644 index 00000000..f3a7e789 --- /dev/null +++ b/src/backenddb/pg_select_wirewatch_accounts.c @@ -0,0 +1,147 @@ +/* + This file is part of TALER + Copyright (C) 2022, 2023 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 + */ +/** + * @file backenddb/pg_select_wirewatch_accounts.c + * @brief Implementation of the select_wirewatch_accounts function for Postgres + * @author Christian Grothoff + */ +#include "platform.h" +#include +#include +#include +#include "pg_select_wirewatch_accounts.h" +#include "pg_helper.h" + + +/** + * Closure for #handle_results(). + */ +struct Context +{ + /** + * Function to call with results. + */ + TALER_MERCHANTDB_WirewatchWorkCallback cb; + + /** + * Closure for @e cb. + */ + void *cb_cls; + + /** + * Set to true if the parsing failed. + */ + bool failure; +}; + + +/** + * Function to be called with the results of a SELECT statement + * that has returned @a num_results results about accounts. + * + * @param cls of type `struct Context *` + * @param result the postgres result + * @param num_results the number of results in @a result + */ +static void +handle_results (void *cls, + PGresult *result, + unsigned int num_results) +{ + struct Context *ctx = cls; + + for (unsigned int i = 0; i < num_results; i++) + { + char *instance; + char *payto_uri; + char *facade_url; + json_t *credential; + uint64_t last_serial; + struct GNUNET_PQ_ResultSpec rs[] = { + GNUNET_PQ_result_spec_auto_from_type ("instance_id", + &instance), + GNUNET_PQ_result_spec_string ("payto_uri", + &payto_uri), + GNUNET_PQ_result_spec_string ("credit_facade_url", + &facade_url), + GNUNET_PQ_result_spec_allow_null ( + TALER_PQ_result_spec_json ("credit_facade_credentials", + &credential), + NULL), + GNUNET_PQ_result_spec_uint64 ("last_bank_serial", + &last_serial), + GNUNET_PQ_result_spec_end + }; + + if (GNUNET_OK != + GNUNET_PQ_extract_result (result, + rs, + i)) + { + GNUNET_break (0); + ctx->failure = true; + return; + } + ctx->cb (ctx->cb_cls, + instance, + payto_uri, + facade_url, + credential, + last_serial); + GNUNET_PQ_cleanup_result (rs); + } +} + + +enum GNUNET_DB_QueryStatus +TMH_PG_select_wirewatch_accounts ( + void *cls, + TALER_MERCHANTDB_WirewatchWorkCallback cb, + void *cb_cls) +{ + struct PostgresClosure *pg = cls; + struct Context ctx = { + .cb = cb, + .cb_cls = cb_cls + }; + struct GNUNET_PQ_QueryParam params[] = { + GNUNET_PQ_query_param_end + }; + enum GNUNET_DB_QueryStatus qs; + + PREPARE (pg, + "select_wirewatch_progress", + "SELECT" + " last_bank_serial" + ",instance_id" + ",payto_uri" + ",credit_facade_url" + ",credit_facade_credentials" + " FROM merchant_accounts" + " JOIN merchant_instances" + " USING (merchant_serial)" + " WHERE active" + " AND credit_facade_url IS NOT NULL"); + check_connection (pg); + qs = GNUNET_PQ_eval_prepared_multi_select (pg->conn, + "select_wirewatch_progress", + params, + &handle_results, + &ctx); + if (ctx.failure) + return GNUNET_DB_STATUS_HARD_ERROR; + return qs; +} diff --git a/src/backenddb/pg_select_wirewatch_accounts.h b/src/backenddb/pg_select_wirewatch_accounts.h new file mode 100644 index 00000000..cff263d3 --- /dev/null +++ b/src/backenddb/pg_select_wirewatch_accounts.h @@ -0,0 +1,44 @@ +/* + This file is part of TALER + Copyright (C) 2022 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 + */ +/** + * @file backenddb/pg_select_wirewatch_accounts.h + * @brief implementation of the select_wirewatch_accounts function for Postgres + * @author Christian Grothoff + */ +#ifndef PG_SELECT_WIREWATCH_ACCOUNTS_H +#define PG_SELECT_WIREWATCH_ACCOUNTS_H + +#include +#include +#include "taler_merchantdb_plugin.h" + + +/** + * Select information about progress made by taler-merchant-wirewatch. + * + * @param cls closure + * @param cb function to call with results + * @param cb_cls closure for @a cb + * @return transaction status + */ +enum GNUNET_DB_QueryStatus +TMH_PG_select_wirewatch_accounts ( + void *cls, + TALER_MERCHANTDB_WirewatchWorkCallback cb, + void *cb_cls); + + +#endif diff --git a/src/backenddb/pg_select_wirewatch_progress.c b/src/backenddb/pg_select_wirewatch_progress.c deleted file mode 100644 index c8049e14..00000000 --- a/src/backenddb/pg_select_wirewatch_progress.c +++ /dev/null @@ -1,56 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 - */ -/** - * @file backenddb/pg_select_wirewatch_progress.c - * @brief Implementation of the select_wirewatch_progress function for Postgres - * @author Christian Grothoff - */ -#include "platform.h" -#include -#include -#include -#include "pg_select_wirewatch_progress.h" -#include "pg_helper.h" - - -enum GNUNET_DB_QueryStatus -TMH_PG_select_wirewatch_progress ( - void *cls, - const char *section, - uint64_t *last_serial) -{ - struct PostgresClosure *pg = cls; - struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (section), - GNUNET_PQ_query_param_end - }; - struct GNUNET_PQ_ResultSpec rs[] = { - GNUNET_PQ_result_spec_uint64 ("last_bank_serial", - last_serial), - GNUNET_PQ_result_spec_end - }; - - PREPARE (pg, - "select_wirewatch_progress", - "SELECT last_bank_serial" - " FROM merchant_wirewatch" - " WHERE account_section=$1"); - check_connection (pg); - return GNUNET_PQ_eval_prepared_singleton_select (pg->conn, - "select_wirewatch_progress", - params, - rs); -} diff --git a/src/backenddb/pg_select_wirewatch_progress.h b/src/backenddb/pg_select_wirewatch_progress.h deleted file mode 100644 index e2cda0a8..00000000 --- a/src/backenddb/pg_select_wirewatch_progress.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - This file is part of TALER - Copyright (C) 2022 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 - */ -/** - * @file backenddb/pg_select_wirewatch_progress.h - * @brief implementation of the select_wirewatch_progress function for Postgres - * @author Christian Grothoff - */ -#ifndef PG_SELECT_WIREWATCH_PROGRESS_H -#define PG_SELECT_WIREWATCH_PROGRESS_H - -#include -#include -#include "taler_merchantdb_plugin.h" - - -/** - * Select information about progress made by taler-merchant-wirewatch. - * - * @param cls closure - * @param section configuration section of the taler-merchant-wirewatch - * @param[out] last_serial set to last serial imported from the bank - * @return transaction status - */ -enum GNUNET_DB_QueryStatus -TMH_PG_select_wirewatch_progress ( - void *cls, - const char *section, - uint64_t *last_serial); - - -#endif diff --git a/src/backenddb/pg_update_wirewatch_progress.c b/src/backenddb/pg_update_wirewatch_progress.c index d702c314..8ffdfe70 100644 --- a/src/backenddb/pg_update_wirewatch_progress.c +++ b/src/backenddb/pg_update_wirewatch_progress.c @@ -29,21 +29,27 @@ enum GNUNET_DB_QueryStatus TMH_PG_update_wirewatch_progress ( void *cls, - const char *section, + const char *instance, + const char *payto_uri, uint64_t last_serial) { struct PostgresClosure *pg = cls; struct GNUNET_PQ_QueryParam params[] = { - GNUNET_PQ_query_param_string (section), + GNUNET_PQ_query_param_string (instance), + GNUNET_PQ_query_param_string (payto_uri), GNUNET_PQ_query_param_uint64 (&last_serial), GNUNET_PQ_query_param_end }; PREPARE (pg, "update_wirewatch_progress", - "UPDATE merchant_wirewatch" - " SET last_bank_serial=$2" - " WHERE account_section=$1"); + "UPDATE merchant_accounts" + " SET last_bank_serial=$3" + " WHERE payto_uri=$2" + " AND merchant_serial =" + " (SELECT merchant_serial" + " FROM merchant_instances" + " WHERE merchant_id=$1)"); check_connection (pg); return GNUNET_PQ_eval_prepared_non_select (pg->conn, "update_wirewatch_progress", diff --git a/src/backenddb/pg_update_wirewatch_progress.h b/src/backenddb/pg_update_wirewatch_progress.h index f50e8066..0e762adc 100644 --- a/src/backenddb/pg_update_wirewatch_progress.h +++ b/src/backenddb/pg_update_wirewatch_progress.h @@ -30,14 +30,16 @@ * Update information about progress made by taler-merchant-wirewatch. * * @param cls closure - * @param section configuration section of the taler-merchant-wirewatch - * @param last_serial last serial imported from the bank + * @param instance name of the instance to record progress for + * @param payto_uri bank account URI to record progress for + * @param last_serial latest serial of a transaction that was processed * @return transaction status */ enum GNUNET_DB_QueryStatus TMH_PG_update_wirewatch_progress ( void *cls, - const char *section, + const char *instance, + const char *payto_uri, uint64_t last_serial); diff --git a/src/backenddb/plugin_merchantdb_postgres.c b/src/backenddb/plugin_merchantdb_postgres.c index 4728801f..19bd022a 100644 --- a/src/backenddb/plugin_merchantdb_postgres.c +++ b/src/backenddb/plugin_merchantdb_postgres.c @@ -32,9 +32,8 @@ #include "pg_helper.h" #include "pg_lookup_instances.h" #include "pg_lookup_transfers.h" -#include "pg_insert_wirewatch_progress.h" #include "pg_update_wirewatch_progress.h" -#include "pg_select_wirewatch_progress.h" +#include "pg_select_wirewatch_accounts.h" /** @@ -7395,8 +7394,8 @@ postgres_connect (void *cls) GNUNET_PQ_make_prepare ("inactivate_account", "UPDATE merchant_accounts SET" " active=FALSE" - " WHERE h_wire=$2 AND" - " merchant_serial=" + " WHERE h_wire=$2" + " AND merchant_serial=" " (SELECT merchant_serial" " FROM merchant_instances" " WHERE merchant_id=$1)"), @@ -9802,12 +9801,10 @@ libtaler_plugin_merchantdb_postgres_init (void *cls) = &TMH_PG_lookup_instance; plugin->lookup_transfers = &TMH_PG_lookup_transfers; - plugin->insert_wirewatch_progress - = &TMH_PG_insert_wirewatch_progress; plugin->update_wirewatch_progress = &TMH_PG_update_wirewatch_progress; - plugin->select_wirewatch_progress - = &TMH_PG_select_wirewatch_progress; + plugin->select_wirewatch_accounts + = &TMH_PG_select_wirewatch_accounts; plugin->store_wire_fee_by_exchange = &postgres_store_wire_fee_by_exchange; plugin->insert_reserve = &postgres_insert_reserve; plugin->activate_reserve = &postgres_activate_reserve; diff --git a/src/bank/mb_parse.c b/src/bank/mb_parse.c index eef303f3..bb668e48 100644 --- a/src/bank/mb_parse.c +++ b/src/bank/mb_parse.c @@ -21,6 +21,21 @@ */ #include "platform.h" #include "taler_merchant_bank_lib.h" +#include + + +/** + * Names of authentication methods available. + */ +static const struct +{ + const char *m; + enum TALER_MERCHANT_BANK_AuthenticationMethod e; +} methods[] = { + { "NONE", TALER_MERCHANT_BANK_AUTH_NONE }, + { "BASIC", TALER_MERCHANT_BANK_AUTH_BASIC }, + { NULL, TALER_MERCHANT_BANK_AUTH_NONE } +}; enum GNUNET_GenericReturnValue @@ -29,15 +44,6 @@ TALER_MERCHANT_BANK_auth_parse_cfg ( const char *section, struct TALER_MERCHANT_BANK_AuthenticationData *auth) { - const struct - { - const char *m; - enum TALER_MERCHANT_BANK_AuthenticationMethod e; - } methods[] = { - { "NONE", TALER_MERCHANT_BANK_AUTH_NONE }, - { "BASIC", TALER_MERCHANT_BANK_AUTH_BASIC }, - { NULL, TALER_MERCHANT_BANK_AUTH_NONE } - }; char *method; if (GNUNET_OK != @@ -115,6 +121,67 @@ TALER_MERCHANT_BANK_auth_parse_cfg ( } +enum GNUNET_GenericReturnValue +TALER_MERCHANT_BANK_auth_parse_json ( + const json_t *cred, + const char *backend_url, + struct TALER_MERCHANT_BANK_AuthenticationData *auth) +{ + const char *method; + + auth->wire_gateway_url = GNUNET_strdup (backend_url); + method = json_string_value (json_object_get (cred, + "type")); + for (unsigned int i = 0; NULL != methods[i].m; i++) + { + if (0 == strcasecmp (method, + methods[i].m)) + { + switch (methods[i].e) + { + case TALER_MERCHANT_BANK_AUTH_NONE: + auth->method = TALER_MERCHANT_BANK_AUTH_NONE; + return GNUNET_OK; + case TALER_MERCHANT_BANK_AUTH_BASIC: + { + const char *username; + const char *password; + struct GNUNET_JSON_Specification spec[] = { + GNUNET_JSON_spec_string ("username", + &username), + GNUNET_JSON_spec_string ("password", + &password), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + const char *err; + unsigned int eline; + + res = GNUNET_JSON_parse (cred, + spec, + &err, + &eline); + if (GNUNET_OK != res) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Credentials malformed: %s (%u)\n", + err, + eline); + GNUNET_free (auth->wire_gateway_url); + return GNUNET_SYSERR; + } + auth->details.basic.username = GNUNET_strdup (username); + auth->details.basic.password = GNUNET_strdup (password); + } + auth->method = TALER_MERCHANT_BANK_AUTH_BASIC; + return GNUNET_OK; + } + } + } + return GNUNET_SYSERR; +} + + void TALER_MERCHANT_BANK_auth_free ( struct TALER_MERCHANT_BANK_AuthenticationData *auth) @@ -124,20 +191,11 @@ TALER_MERCHANT_BANK_auth_free ( case TALER_MERCHANT_BANK_AUTH_NONE: break; case TALER_MERCHANT_BANK_AUTH_BASIC: - if (NULL != auth->details.basic.username) - { - GNUNET_free (auth->details.basic.username); - auth->details.basic.username = NULL; - } - if (NULL != auth->details.basic.password) - { - GNUNET_free (auth->details.basic.password); - auth->details.basic.password = NULL; - } + GNUNET_free (auth->details.basic.username); + GNUNET_free (auth->details.basic.password); break; } GNUNET_free (auth->wire_gateway_url); - auth->wire_gateway_url = NULL; } diff --git a/src/include/taler_merchant_bank_lib.h b/src/include/taler_merchant_bank_lib.h index e295f0b3..beaaa516 100644 --- a/src/include/taler_merchant_bank_lib.h +++ b/src/include/taler_merchant_bank_lib.h @@ -216,6 +216,22 @@ TALER_MERCHANT_BANK_auth_parse_cfg ( struct TALER_MERCHANT_BANK_AuthenticationData *auth); +/** + * Convenience method for parsing JSON with bank + * authentication data. + * + * @param cred configuration to parse + * @param backend_url URL of the backend (not in the JSON) + * @param[out] auth set to the configuration data found + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TALER_MERCHANT_BANK_auth_parse_json ( + const json_t *cred, + const char *backend_url, + struct TALER_MERCHANT_BANK_AuthenticationData *auth); + + /** * Free memory inside of @a auth (but not @a auth itself). * Dual to #TALER_MERCHANT_BANK_auth_parse_cfg(). diff --git a/src/include/taler_merchantdb_plugin.h b/src/include/taler_merchantdb_plugin.h index c1d5ae27..cffb9acd 100644 --- a/src/include/taler_merchantdb_plugin.h +++ b/src/include/taler_merchantdb_plugin.h @@ -98,6 +98,19 @@ struct TALER_MERCHANTDB_AccountDetails */ const char *payto_uri; + /** + * Where can the taler-merchant-wirewatch helper + * download information about incoming transfers? + * NULL if not available. + */ + const char *credit_facade_url; + + /** + * JSON with credentials to use to access the + * @e credit_facade_url. + */ + const json_t *credit_facade_credentials; + /** * Is the account set for active use in new contracts? */ @@ -713,6 +726,27 @@ typedef void const struct TALER_TrackTransferDetails *ttd); +/** + * Function called with information about a accounts + * the wirewatcher should monitor. + * + * @param cls closure + * @param instance instance that owns the account + * @param payto_uri account URI + * @param credit_facade_url URL for the credit facade + * @param credit_facade_credentials account access credentials + * @param last_serial last transaction serial (inclusive) we have seen from this account + */ +typedef void +(*TALER_MERCHANTDB_WirewatchWorkCallback)( + void *cls, + const char *instance, + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials, + uint64_t last_serial); + + /** * Function called with information about a wire transfer. * @@ -1998,47 +2032,36 @@ struct TALER_MERCHANTDB_Plugin const char *session_id, char **order_id); - /** - * Insert information about progress made by taler-merchant-wirewatch. - * - * @param cls closure - * @param section configuration section of the taler-merchant-wirewatch - * @param last_serial last serial imported from the bank - * @return transaction status - */ - enum GNUNET_DB_QueryStatus - (*insert_wirewatch_progress)( - void *cls, - const char *section, - uint64_t last_serial); - /** * Update information about progress made by taler-merchant-wirewatch. * * @param cls closure - * @param section configuration section of the taler-merchant-wirewatch + * @param instance which instance does the account belong to + * @param payto_uri which account is this about * @param last_serial last serial imported from the bank * @return transaction status */ enum GNUNET_DB_QueryStatus (*update_wirewatch_progress)( void *cls, - const char *section, + const char *instance, + const char *payto_uri, uint64_t last_serial); + /** - * Select information about progress made by taler-merchant-wirewatch. + * Select information about accounts which taler-merchant-wirewatch should work on. * * @param cls closure - * @param section configuration section of the taler-merchant-wirewatch - * @param[out] last_serial set to last serial imported from the bank + * @param cb function to call with results + * @param cb_cls closure for @a cb * @return transaction status */ enum GNUNET_DB_QueryStatus - (*select_wirewatch_progress)( + (*select_wirewatch_accounts)( void *cls, - const char *section, - uint64_t *last_serial); + TALER_MERCHANTDB_WirewatchWorkCallback cb, + void *cb_cls); /** -- cgit v1.2.3