diff options
author | Christian Grothoff <christian@grothoff.org> | 2023-04-23 22:57:55 +0200 |
---|---|---|
committer | Christian Grothoff <christian@grothoff.org> | 2023-04-23 22:57:55 +0200 |
commit | 154f964aab5df00cbadaa015474c2b323161aa96 (patch) | |
tree | 084f300e664fb96d0693baf73434ed245ef56641 /src/backend | |
parent | 889595f986d922ffbcdcd746fdfad7f1a0e53595 (diff) | |
download | merchant-154f964aab5df00cbadaa015474c2b323161aa96.tar.gz merchant-154f964aab5df00cbadaa015474c2b323161aa96.tar.bz2 merchant-154f964aab5df00cbadaa015474c2b323161aa96.zip |
API change for #6363
Diffstat (limited to 'src/backend')
-rw-r--r-- | src/backend/Makefile.am | 1 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 2 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.h | 12 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_helper.c | 223 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_helper.h | 44 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-delete-instances-ID.c | 12 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-get-instances-ID.c | 3 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-patch-instances-ID.c | 197 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_private-post-instances.c | 169 | ||||
-rw-r--r-- | src/backend/taler-merchant-wirewatch.c | 45 |
10 files changed, 513 insertions, 195 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index 89fab12c..d5bcda16 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -135,6 +135,7 @@ taler_merchant_httpd_SOURCES = \ taler_merchant_httpd_LDADD = \ $(top_builddir)/src/backenddb/libtalermerchantdb.la \ + $(top_builddir)/src/bank/libtalermerchantbank.la \ -ltalerexchange \ -ltalertemplating \ -ltalermhd \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index bebe9f32..8f455f58 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -240,6 +240,8 @@ TMH_instance_decref (struct TMH_MerchantInstance *mi) wm); GNUNET_free (wm->payto_uri); GNUNET_free (wm->wire_method); + GNUNET_free (wm->credit_facade_url); + json_decref (wm->credit_facade_credentials); GNUNET_free (wm); } diff --git a/src/backend/taler-merchant-httpd.h b/src/backend/taler-merchant-httpd.h index 391987ab..6f637934 100644 --- a/src/backend/taler-merchant-httpd.h +++ b/src/backend/taler-merchant-httpd.h @@ -25,6 +25,7 @@ #include "taler_merchantdb_lib.h" #include <taler/taler_mhd_lib.h> #include <gnunet/gnunet_mhd_compat.h> +#include "taler_merchant_bank_lib.h" /** * Shorthand for exit jumps. @@ -71,6 +72,17 @@ struct TMH_WireMethod struct TALER_MerchantWireHashP h_wire; /** + * Base URL of the credit facade. + */ + char *credit_facade_url; + + /** + * Authentication data to access the credit facade. + * May be uninitialized if not provided by the client. + */ + json_t *credit_facade_credentials; + + /** * Is this wire method active (should it be included in new contracts)? */ bool active; diff --git a/src/backend/taler-merchant-httpd_helper.c b/src/backend/taler-merchant-httpd_helper.c index 149ff3c3..981f5937 100644 --- a/src/backend/taler-merchant-httpd_helper.c +++ b/src/backend/taler-merchant-httpd_helper.c @@ -1,6 +1,6 @@ /* This file is part of TALER - (C) 2014--2021 Taler Systems SA + (C) 2014--2023 Taler Systems SA TALER is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software @@ -27,68 +27,177 @@ #include <taler/taler_templating_lib.h> #include <taler/taler_dbevents.h> -/** - * check @a payto_uris for well-formedness - * - * @param payto_uris JSON array of payto URIs (presumably) - * @return true if they are all valid URIs (and this is an array of strings) - */ + +enum GNUNET_GenericReturnValue +TMH_cmp_wire_account ( + const json_t *account, + const struct TMH_WireMethod *wm) +{ + const char *credit_facade_url = NULL; + json_t *credit_facade_credentials = NULL; + const char *uri; + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ("payto_uri", + &uri), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("credit_facade_url", + &credit_facade_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("credit_facade_credentials", + &credit_facade_credentials), + NULL), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + const char *ename; + unsigned int eline; + + res = GNUNET_JSON_parse (account, + ispec, + &ename, + &eline); + if (GNUNET_OK != res) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to parse account spec: %s (%u)\n", + ename, + eline); + return GNUNET_SYSERR; + } + if (0 != + strcmp (wm->payto_uri, + uri)) + { + GNUNET_JSON_parse_free (ispec); + return GNUNET_SYSERR; + } + if ( (NULL == credit_facade_url) != + (NULL == wm->credit_facade_url) || + (NULL == credit_facade_credentials) != + (NULL == wm->credit_facade_credentials) ) + { + GNUNET_JSON_parse_free (ispec); + return GNUNET_NO; + } + if ( (NULL != credit_facade_url) && + (0 != strcmp (credit_facade_url, + wm->credit_facade_url)) ) + { + GNUNET_JSON_parse_free (ispec); + return GNUNET_NO; + } + if ( (NULL != credit_facade_credentials) && + (0 != json_equal (credit_facade_credentials, + wm->credit_facade_credentials)) ) + { + GNUNET_JSON_parse_free (ispec); + return GNUNET_NO; + } + GNUNET_JSON_parse_free (ispec); + return GNUNET_YES; +} + + bool -TMH_payto_uri_array_valid (const json_t *payto_uris) +TMH_accounts_array_valid (const json_t *accounts) { - bool payto_ok = true; + unsigned int len; - if (! json_is_array (payto_uris)) + if (! json_is_array (accounts)) { GNUNET_break_op (0); - payto_ok = false; + return false; } - else + len = json_array_size (accounts); + for (unsigned int i = 0; i<len; i++) { - unsigned int len = json_array_size (payto_uris); + json_t *payto_uri = json_array_get (accounts, + i); + const char *credit_facade_url = NULL; + json_t *credit_facade_credentials = NULL; + const char *uri; + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ("payto_uri", + &uri), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("credit_facade_url", + &credit_facade_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("credit_facade_credentials", + &credit_facade_credentials), + NULL), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + const char *ename; + unsigned int eline; + + res = GNUNET_JSON_parse (payto_uri, + ispec, + &ename, + &eline); + if (GNUNET_OK != res) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Failed to parse account spec: %s (%u)\n", + ename, + eline); + return false; + } - for (unsigned int i = 0; i<len; i++) + /* Test for the same payto:// URI being given twice */ + for (unsigned int j = 0; j<i; j++) { - json_t *payto_uri = json_array_get (payto_uris, - i); - const char *uri; - - if (! json_is_string (payto_uri)) - payto_ok = false; - uri = json_string_value (payto_uri); - /* Test for the same payto:// URI being given twice */ - for (unsigned int j = 0; j<i; j++) + json_t *old_uri = json_array_get (accounts, + j); + if (0 == strcmp (uri, + json_string_value ( + json_object_get (old_uri, + "payto_uri")))) { - json_t *old_uri = json_array_get (payto_uris, - j); - if (json_equal (payto_uri, - old_uri)) - { - GNUNET_break_op (0); - payto_ok = false; - break; - } + GNUNET_break_op (0); + GNUNET_JSON_parse_free (ispec); + return false; } - if (! payto_ok) - break; + } + { + char *err; + + if (NULL != + (err = TALER_payto_validate (uri))) { - char *err; - - if (NULL != - (err = TALER_payto_validate (uri))) - { - GNUNET_log (GNUNET_ERROR_TYPE_WARNING, - "Encountered invalid payto://-URI `%s': %s\n", - uri, - err); - GNUNET_free (err); - payto_ok = false; - break; - } + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Encountered invalid payto://-URI `%s': %s\n", + uri, + err); + GNUNET_free (err); + GNUNET_JSON_parse_free (ispec); + return false; } } + if ( (NULL != credit_facade_url) || + (NULL != credit_facade_credentials) ) + { + struct TALER_MERCHANT_BANK_AuthenticationData auth; + + if (GNUNET_OK != + TALER_MERCHANT_BANK_auth_parse_json (credit_facade_credentials, + credit_facade_url, + &auth)) + { + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "Invalid credit facade URL or credentials `%s'\n", + credit_facade_url); + GNUNET_JSON_parse_free (ispec); + return false; + } + TALER_MERCHANT_BANK_auth_free (&auth); + } + GNUNET_JSON_parse_free (ispec); } - return payto_ok; + return true; } @@ -396,14 +505,17 @@ TMH_taxes_array_valid (const json_t *taxes) struct TMH_WireMethod * -TMH_setup_wire_account (const char *payto_uri) +TMH_setup_wire_account ( + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials) { struct TMH_WireMethod *wm; char *emsg; if (NULL != (emsg = TALER_payto_validate (payto_uri))) { - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, "Invalid URI `%s': %s\n", payto_uri, emsg); @@ -412,6 +524,12 @@ TMH_setup_wire_account (const char *payto_uri) } wm = GNUNET_new (struct TMH_WireMethod); + if (NULL != credit_facade_url) + wm->credit_facade_url + = GNUNET_strdup (credit_facade_url); + if (NULL != credit_facade_credentials) + wm->credit_facade_credentials + = json_incref ((json_t*) credit_facade_credentials); GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, &wm->wire_salt, sizeof (wm->wire_salt)); @@ -472,8 +590,9 @@ TMH_check_auth_config (struct MHD_Connection *connection, TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_BAD_AUTH, - "bad authentication config")) ? - GNUNET_NO : GNUNET_SYSERR; + "bad authentication config")) + ? GNUNET_NO + : GNUNET_SYSERR; } return GNUNET_OK; } diff --git a/src/backend/taler-merchant-httpd_helper.h b/src/backend/taler-merchant-httpd_helper.h index 5cee97cc..14156fce 100644 --- a/src/backend/taler-merchant-httpd_helper.h +++ b/src/backend/taler-merchant-httpd_helper.h @@ -28,13 +28,13 @@ #include "taler-merchant-httpd.h" /** - * check @a payto_uris for well-formedness + * check @a accounts for well-formedness * - * @param payto_uris JSON array of payto URIs (presumably) - * @return true if they are all valid URIs (and this is an array of strings) + * @param accounts JSON array of merchant accounts (presumably) + * @return true if they are all valid accounts */ bool -TMH_payto_uri_array_valid (const json_t *payto_uris); +TMH_accounts_array_valid (const json_t *accounts); /** @@ -103,15 +103,45 @@ TMH_template_contract_valid (const json_t *template_contract); * Setup new wire method for the given @ payto_uri. * * @param payto_uri already validated payto URI + * @param credit_facade_url where to download credit information for this account (can be NULL) + * @param credit_facade_credentials credentials for the @a credit_facade_url * @return new wire method object, never fails */ struct TMH_WireMethod * -TMH_setup_wire_account (const char *payto_uri); +TMH_setup_wire_account ( + const char *payto_uri, + const char *credit_facade_url, + const json_t *credit_facade_credentials); + /** - * FIXME: document + * Test if JSON spec @a account for a wire method is equal to the given @a wm. + * + * @param account JSON spec for a merchant account + * @param wm known wire method + * @return #GNUNET_YES if both specifications are equal + * #GNUNET_NO if the specifications are for + * the same account but differ in the credit facade + * #GNUNET_SYSERR if the specs are for different accounts + * or if @a account is malformed */ +enum GNUNET_GenericReturnValue +TMH_cmp_wire_account ( + const json_t *account, + const struct TMH_WireMethod *wm); + +/** + * Check that the provided authentication configuration + * is valid. + * + * @param connection connection to use for returning errors + * @param jauth JSON with authentication data + * @param[out] auth_token set to the authentication token + * @return #GNUNET_OK on success, + * #GNUNET_NO if an error was returned on @a connection + * #GNUNET_SYSERR if we failed to return an error on @a connection + */ enum GNUNET_GenericReturnValue TMH_check_auth_config (struct MHD_Connection *connection, const json_t *jauth, @@ -144,7 +174,6 @@ TMH_uuid_from_string (const char *uuids, GNUNET_JSON_pack_object_incref ("exchange_reply", (json_t *) (hr)->reply)) - /** * TMH_trigger_webhook is a function that need to be use when someone * pay. Merchant need to have a notification. @@ -159,5 +188,4 @@ TMH_trigger_webhook (const char *instance, const json_t *args); - #endif diff --git a/src/backend/taler-merchant-httpd_private-delete-instances-ID.c b/src/backend/taler-merchant-httpd_private-delete-instances-ID.c index 2791d6d7..8862eadd 100644 --- a/src/backend/taler-merchant-httpd_private-delete-instances-ID.c +++ b/src/backend/taler-merchant-httpd_private-delete-instances-ID.c @@ -21,6 +21,7 @@ #include "platform.h" #include "taler-merchant-httpd_private-delete-instances-ID.h" #include <taler/taler_json_lib.h> +#include <taler/taler_dbevents.h> /** @@ -52,6 +53,17 @@ delete_instances_ID (struct TMH_MerchantInstance *mi, else qs = TMH_db->delete_instance_private_key (TMH_db->cls, mi->settings.id); + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) + }; + + TMH_db->event_notify (TMH_db->cls, + &es, + NULL, + 0); + } switch (qs) { case GNUNET_DB_STATUS_HARD_ERROR: diff --git a/src/backend/taler-merchant-httpd_private-get-instances-ID.c b/src/backend/taler-merchant-httpd_private-get-instances-ID.c index c51d3de0..d2e3d937 100644 --- a/src/backend/taler-merchant-httpd_private-get-instances-ID.c +++ b/src/backend/taler-merchant-httpd_private-get-instances-ID.c @@ -52,6 +52,9 @@ get_instances_ID (struct TMH_MerchantInstance *mi, GNUNET_JSON_pack_string ( "payto_uri", wm->payto_uri), + GNUNET_JSON_pack_string ( + "credit_facade_url", + wm->credit_facade_url), GNUNET_JSON_pack_data_auto ("h_wire", &wm->h_wire), GNUNET_JSON_pack_data_auto ( diff --git a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c index 77a75da4..9241d069 100644 --- a/src/backend/taler-merchant-httpd_private-patch-instances-ID.c +++ b/src/backend/taler-merchant-httpd_private-patch-instances-ID.c @@ -26,6 +26,7 @@ #include "taler-merchant-httpd_private-patch-instances-ID.h" #include "taler-merchant-httpd_helper.h" #include <taler/taler_json_lib.h> +#include <taler/taler_dbevents.h> /** @@ -62,14 +63,14 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, struct TMH_HandlerContext *hc) { struct TALER_MERCHANTDB_InstanceSettings is; - json_t *payto_uris; + json_t *accounts; const char *name; const char *uts = "business"; struct TMH_WireMethod *wm_head = NULL; struct TMH_WireMethod *wm_tail = NULL; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("payto_uris", - &payto_uris), + GNUNET_JSON_spec_json ("accounts", + &accounts), GNUNET_JSON_spec_string ("name", &name), GNUNET_JSON_spec_mark_optional ( @@ -167,7 +168,7 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, "jurisdiction"); } - if (! TMH_payto_uri_array_valid (payto_uris)) + if (! TMH_accounts_array_valid (accounts)) { GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_with_error (connection, @@ -175,7 +176,7 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, TALER_EC_GENERIC_PAYTO_URI_MALFORMED, NULL); } - for (unsigned int i = 0; i<MAX_RETRIES; i++) + for (unsigned int retry = 0; retry<MAX_RETRIES; retry++) { /* Cleanup after earlier loops */ { @@ -255,44 +256,59 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, /* Check for changes in accounts */ { - unsigned int len = json_array_size (payto_uris); + unsigned int len = json_array_size (accounts); struct TMH_WireMethod *matches[GNUNET_NZL (len)]; - bool matched; + bool updated[GNUNET_NZL (len)]; memset (matches, 0, sizeof (matches)); + memset (updated, + 0, + sizeof (updated)); for (struct TMH_WireMethod *wm = mi->wm_head; NULL != wm; wm = wm->next) { - const char *uri = wm->payto_uri; - - GNUNET_assert (NULL != uri); - matched = false; + bool matched = false; for (unsigned int i = 0; i<len; i++) { - const char *str = json_string_value (json_array_get (payto_uris, - i)); - if (0 == strcasecmp (uri, - str)) + json_t *account = json_array_get (accounts, + i); + enum GNUNET_GenericReturnValue ret; + + ret = TMH_cmp_wire_account (account, + wm); + switch (ret) { + case GNUNET_SYSERR: + continue; + case GNUNET_NO: + matched = true; /* our own existing payto URIs should be unique, that is no duplicates in the list, so we cannot match twice */ GNUNET_assert (NULL == matches[i]); matches[i] = wm; + updated[i] = true; + break; + case GNUNET_YES: matched = true; + /* our own existing payto URIs should be unique, that is no + duplicates in the list, so we cannot match twice */ + GNUNET_assert (NULL == matches[i]); + matches[i] = wm; break; } } + /* delete unmatched (= removed) accounts */ if ( (! matched) && (wm->active) ) { /* Account was REMOVED */ GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Existing account `%s' not found, inactivating it.\n", - uri); + "Existing account `%s' not found, deactivating it.\n", + wm->payto_uri); wm->deleting = true; qs = TMH_db->inactivate_account (TMH_db->cls, mi->settings.id, @@ -306,16 +322,51 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, goto giveup; } } - } - /* Find _new_ accounts */ + } /* for (wm) */ + + /* handle updates */ for (unsigned int i = 0; i<len; i++) { - struct TALER_MERCHANTDB_AccountDetails ad; - struct TMH_WireMethod *wm; + struct TMH_WireMethod *wm = matches[i]; - if (NULL != matches[i]) + if (! updated[i]) + continue; + GNUNET_assert (NULL != wm); { - wm = matches[i]; + struct TALER_MERCHANTDB_AccountDetails ad = { + .payto_uri = wm->payto_uri, + .h_wire = wm->h_wire, + .salt = wm->wire_salt, + .credit_facade_url = wm->credit_facade_url, + .credit_facade_credentials = wm->credit_facade_credentials, + .active = true + }; + + qs = TMH_db->update_account (TMH_db->cls, + mi->settings.id, + &ad); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + TMH_db->rollback (TMH_db->cls); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto retry; + else + goto giveup; + } + } /* ad scope */ + } /* for possible updates */ + + /* Find _new_ accounts or accounts to only enable */ + for (unsigned int i = 0; i<len; i++) + { + json_t *account = json_array_get (accounts, + i); + struct TMH_WireMethod *wm = matches[i]; + + if (NULL != wm) + { + if (updated[i]) + continue; /* handled above */ if (! wm->active) { qs = TMH_db->activate_account (TMH_db->cls, @@ -333,34 +384,82 @@ patch_instances_ID (struct TMH_MerchantInstance *mi, wm->enabling = true; continue; } - ad.payto_uri = json_string_value (json_array_get (payto_uris, - i)); - GNUNET_assert (NULL != ad.payto_uri); - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Adding NEW account `%s'\n", - ad.payto_uri); - wm = TMH_setup_wire_account (ad.payto_uri); - GNUNET_assert (NULL != wm); /* checked payto_uri validity earlier */ - GNUNET_CONTAINER_DLL_insert (wm_head, - wm_tail, - wm); - ad.h_wire = wm->h_wire; - ad.salt = wm->wire_salt; - ad.active = true; - qs = TMH_db->insert_account (TMH_db->cls, - mi->settings.id, - &ad); - if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { - TMH_db->rollback (TMH_db->cls); - if (GNUNET_DB_STATUS_SOFT_ERROR == qs) - goto retry; - else - goto giveup; - } - } - } + const char *credit_facade_url = NULL; + json_t *credit_facade_credentials = NULL; + const char *uri; + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ("payto_uri", + &uri), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("credit_facade_url", + &credit_facade_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("credit_facade_credentials", + &credit_facade_credentials), + NULL), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + account, + ispec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Adding NEW account `%s'\n", + uri); + wm = TMH_setup_wire_account (uri, + credit_facade_url, + credit_facade_credentials); + GNUNET_assert (NULL != wm); /* checked payto_uri validity earlier */ + GNUNET_CONTAINER_DLL_insert (wm_head, + wm_tail, + wm); + GNUNET_JSON_parse_free (ispec); + } /* ispec scope */ + { + struct TALER_MERCHANTDB_AccountDetails ad = { + .payto_uri = wm->payto_uri, + .h_wire = wm->h_wire, + .salt = wm->wire_salt, + .credit_facade_url = wm->credit_facade_url, + .credit_facade_credentials = wm->credit_facade_credentials, + .active = true + }; + + qs = TMH_db->insert_account (TMH_db->cls, + mi->settings.id, + &ad); + if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) + { + TMH_db->rollback (TMH_db->cls); + if (GNUNET_DB_STATUS_SOFT_ERROR == qs) + goto retry; + else + goto giveup; + } + } /* ad variable scope */ + } /* for (i) to find new accounts */ + } /* scope for checking for account changes */ + + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) + }; + + TMH_db->event_notify (TMH_db->cls, + &es, + NULL, + 0); + } qs = TMH_db->commit (TMH_db->cls); retry: if (GNUNET_DB_STATUS_SOFT_ERROR == qs) diff --git a/src/backend/taler-merchant-httpd_private-post-instances.c b/src/backend/taler-merchant-httpd_private-post-instances.c index 4d9320ba..c8f443cd 100644 --- a/src/backend/taler-merchant-httpd_private-post-instances.c +++ b/src/backend/taler-merchant-httpd_private-post-instances.c @@ -25,6 +25,8 @@ #include "platform.h" #include "taler-merchant-httpd_private-post-instances.h" #include "taler-merchant-httpd_helper.h" +#include "taler_merchant_bank_lib.h" +#include <taler/taler_dbevents.h> #include <taler/taler_json_lib.h> #include <regex.h> @@ -39,51 +41,45 @@ * URIs as those already in @a mi (possibly in a different order). * * @param mi a merchant instance with accounts - * @param payto_uris a JSON array with accounts (presumably) + * @param accounts a JSON array with accounts (presumably) * @return true if they are 'equal', false if not or of payto_uris is not an array */ static bool accounts_equal (const struct TMH_MerchantInstance *mi, - json_t *payto_uris) + const json_t *accounts) { - if (! json_is_array (payto_uris)) + if (! json_is_array (accounts)) return false; { - unsigned int len = json_array_size (payto_uris); - bool matches[GNUNET_NZL (len)]; - struct TMH_WireMethod *wm; + unsigned int len = json_array_size (accounts); + enum GNUNET_GenericReturnValue matches[GNUNET_NZL (len)]; - memset (matches, - 0, - sizeof (matches)); - for (wm = mi->wm_head; + for (unsigned int i = 0; i<len; i++) + matches[i] = GNUNET_SYSERR; + for (struct TMH_WireMethod *wm = mi->wm_head; NULL != wm; wm = wm->next) { - const char *uri = wm->payto_uri; - - GNUNET_assert (NULL != uri); for (unsigned int i = 0; i<len; i++) { - const char *str = json_string_value (json_array_get (payto_uris, - i)); + json_t *account = json_array_get (accounts, + i); + enum GNUNET_GenericReturnValue ret; - GNUNET_assert (NULL != str); - if (0 == strcasecmp (uri, - str)) + ret = TMH_cmp_wire_account (account, + wm); + if (GNUNET_SYSERR == ret) + continue; + if (GNUNET_SYSERR != matches[i]) { - if (matches[i]) - { - GNUNET_break (0); - return false; /* duplicate entry!? */ - } - matches[i] = true; - break; + GNUNET_break (0); + return false; /* duplicate entry!? */ } + matches[i] = ret; } } for (unsigned int i = 0; i<len; i++) - if (! matches[i]) + if (GNUNET_YES != matches[i]) return false; } return true; @@ -91,48 +87,6 @@ accounts_equal (const struct TMH_MerchantInstance *mi, /** - * Free memory used by @a wm - * - * @param wm wire method to free - */ -static void -free_wm (struct TMH_WireMethod *wm) -{ - GNUNET_free (wm->payto_uri); - GNUNET_free (wm->wire_method); - GNUNET_free (wm); -} - - -/** - * Free memory used by @a mi. - * - * @param mi instance to free - */ -static void -free_mi (struct TMH_MerchantInstance *mi) -{ - struct TMH_WireMethod *wm; - - while (NULL != (wm = mi->wm_head)) - { - GNUNET_CONTAINER_DLL_remove (mi->wm_head, - mi->wm_tail, - wm); - free_wm (wm); - } - GNUNET_free (mi->settings.id); - GNUNET_free (mi->settings.name); - GNUNET_free (mi->settings.website); - GNUNET_free (mi->settings.email); - GNUNET_free (mi->settings.logo); - json_decref (mi->settings.address); - json_decref (mi->settings.jurisdiction); - GNUNET_free (mi); -} - - -/** * Generate an instance, given its configuration. * * @param rh context of the handler @@ -147,15 +101,15 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, { struct TALER_MERCHANTDB_InstanceSettings is; struct TALER_MERCHANTDB_InstanceAuthSettings ias; - json_t *payto_uris; + json_t *accounts; const char *auth_token = NULL; const char *uts = "business"; struct TMH_WireMethod *wm_head = NULL; struct TMH_WireMethod *wm_tail = NULL; json_t *jauth; struct GNUNET_JSON_Specification spec[] = { - GNUNET_JSON_spec_json ("payto_uris", - &payto_uris), + GNUNET_JSON_spec_json ("accounts", + &accounts), GNUNET_JSON_spec_string ("id", (const char **) &is.id), GNUNET_JSON_spec_string ("name", @@ -208,7 +162,6 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, ? MHD_YES : MHD_NO; } - if (NULL == uts) uts = "business"; if (GNUNET_OK != @@ -233,8 +186,8 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, return (GNUNET_NO == ret) ? MHD_YES : MHD_NO; } - /* check payto_uris for well-formedness */ - if (! TMH_payto_uri_array_valid (payto_uris)) + /* check accounts for well-formedness */ + if (! TMH_accounts_array_valid (accounts)) return TALER_MHD_reply_with_error (connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PAYTO_URI_MALFORMED, @@ -341,12 +294,14 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, is.address)) && (1 == json_equal (mi->settings.jurisdiction, is.jurisdiction)) && - (GNUNET_OK == TALER_amount_cmp_currency ( + (GNUNET_OK == + TALER_amount_cmp_currency ( &mi->settings.default_max_deposit_fee, &is.default_max_deposit_fee)) && (0 == TALER_amount_cmp (&mi->settings.default_max_deposit_fee, &is.default_max_deposit_fee)) && - (GNUNET_OK == TALER_amount_cmp_currency ( + (GNUNET_OK == + TALER_amount_cmp_currency ( &mi->settings.default_max_wire_fee, &is.default_max_wire_fee)) && (0 == TALER_amount_cmp (&mi->settings.default_max_wire_fee, @@ -360,7 +315,7 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, ==, is.default_pay_delay)) && (accounts_equal (mi, - payto_uris)) ) + accounts)) ) { GNUNET_JSON_parse_free (spec); return TALER_MHD_reply_static (connection, @@ -382,18 +337,46 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, /* convert provided payto URIs into internal data structure with salts */ { - unsigned int len = json_array_size (payto_uris); + unsigned int len = json_array_size (accounts); for (unsigned int i = 0; i<len; i++) { - json_t *payto_uri = json_array_get (payto_uris, - i); + json_t *account = json_array_get (accounts, + i); + const char *credit_facade_url = NULL; + json_t *credit_facade_credentials = NULL; + const char *uri; struct TMH_WireMethod *wm; - - wm = TMH_setup_wire_account (json_string_value (payto_uri)); + struct GNUNET_JSON_Specification ispec[] = { + GNUNET_JSON_spec_string ("payto_uri", + &uri), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_string ("credit_facade_url", + &credit_facade_url), + NULL), + GNUNET_JSON_spec_mark_optional ( + GNUNET_JSON_spec_json ("credit_facade_credentials", + &credit_facade_credentials), + NULL), + GNUNET_JSON_spec_end () + }; + enum GNUNET_GenericReturnValue res; + + res = TALER_MHD_parse_json_data (connection, + account, + ispec); + if (GNUNET_OK != res) + return (GNUNET_NO == res) + ? MHD_YES + : MHD_NO; + wm = TMH_setup_wire_account (uri, + credit_facade_url, + credit_facade_credentials); + GNUNET_assert (NULL != wm); GNUNET_CONTAINER_DLL_insert (wm_head, wm_tail, wm); + GNUNET_JSON_parse_free (ispec); } } @@ -446,7 +429,8 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, "post /instances")) { GNUNET_JSON_parse_free (spec); - free_mi (mi); + mi->rc = 1; + TMH_instance_decref (mi); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_START_FAILED, @@ -469,7 +453,8 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, TALER_EC_MERCHANT_PRIVATE_POST_INSTANCES_ALREADY_EXISTS, is.id); GNUNET_JSON_parse_free (spec); - free_mi (mi); + mi->rc = 1; + TMH_instance_decref (mi); return ret; } for (struct TMH_WireMethod *wm = wm_head; @@ -480,6 +465,8 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, .payto_uri = wm->payto_uri, .salt = wm->wire_salt, .h_wire = wm->h_wire, + .credit_facade_url = wm->credit_facade_url, + .credit_facade_credentials = wm->credit_facade_credentials, .active = wm->active }; @@ -497,6 +484,17 @@ TMH_private_post_instances (const struct TMH_RequestHandler *rh, break; goto retry; } + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) + }; + + TMH_db->event_notify (TMH_db->cls, + &es, + NULL, + 0); + } qs = TMH_db->commit (TMH_db->cls); if (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) qs = GNUNET_DB_STATUS_SUCCESS_ONE_RESULT; @@ -507,7 +505,8 @@ retry: if (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != qs) { GNUNET_JSON_parse_free (spec); - free_mi (mi); + mi->rc = 1; + TMH_instance_decref (mi); return TALER_MHD_reply_with_error (connection, MHD_HTTP_INTERNAL_SERVER_ERROR, TALER_EC_GENERIC_DB_COMMIT_FAILED, diff --git a/src/backend/taler-merchant-wirewatch.c b/src/backend/taler-merchant-wirewatch.c index 380bcc11..03e317b0 100644 --- a/src/backend/taler-merchant-wirewatch.c +++ b/src/backend/taler-merchant-wirewatch.c @@ -22,6 +22,7 @@ #include <gnunet/gnunet_util_lib.h> #include <jansson.h> #include <pthread.h> +#include <taler/taler_dbevents.h> #include "taler_merchant_bank_lib.h" #include "taler_merchantdb_lib.h" #include "taler_merchantdb_plugin.h" @@ -123,6 +124,12 @@ static struct GNUNET_CURL_Context *ctx; static struct GNUNET_CURL_RescheduleContext *rc; /** + * Event handler to learn that the configuration changed + * and we should shutdown (to be restarted). + */ +static struct GNUNET_DB_EventHandler *eh; + +/** * Value to return from main(). 0 on success, non-zero on errors. */ static int global_ret; @@ -209,6 +216,11 @@ shutdown_task (void *cls) save (w); end_watch (w); } + if (NULL != eh) + { + db_plugin->event_listen_cancel (eh); + eh = NULL; + } TALER_MERCHANTDB_plugin_unload (db_plugin); db_plugin = NULL; cfg = NULL; @@ -457,6 +469,26 @@ start_watch ( /** + * Function called on configuration change events received from Postgres. We + * shutdown (and systemd should restart us). + * + * @param cls closure (NULL) + * @param extra additional event data provided + * @param extra_size number of bytes in @a extra + */ +static void +config_changed (void *cls, + const void *extra, + size_t extra_size) +{ + (void) cls; + (void) extra; + (void) extra_size; + GNUNET_SCHEDULER_shutdown (); +} + + +/** * First task. * * @param cls closure, NULL @@ -501,7 +533,18 @@ run (void *cls, GNUNET_SCHEDULER_shutdown (); return; } - // FIXME: also add notification job! + { + struct GNUNET_DB_EventHeaderP es = { + .size = htons (sizeof (es)), + .type = htons (TALER_DBEVENT_MERCHANT_ACCOUNTS_CHANGED) + }; + + eh = db_plugin->event_listen (db_plugin->cls, + &es, + GNUNET_TIME_UNIT_FOREVER_REL, + &config_changed, + NULL); + } { enum GNUNET_DB_QueryStatus qs; |