exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

commit 14dae4fd53aaadbf7792eda5aa3158b562ccff3a
parent da3bc6a9326b8e0c0c4d6d7af0ec3dc990592f28
Author: Christian Blättler <blatc2@bfh.ch>
Date:   Tue, 21 Nov 2023 08:07:27 +0100

Merge branch 'master' into feature/tokens

Diffstat:
M.gitmodules | 4++++
Mbootstrap | 13+++++++++++++
Mconfigure.ac | 72++++++++++++++++++++++++++++++++++++------------------------------------
Mcontrib/.gitignore | 2++
Dcontrib/Makefile.am | 63---------------------------------------------------------------
Acontrib/Makefile.am.in | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acontrib/wallet-core | 1+
Mdebian/taler-exchange.install | 1+
Msrc/auditor/generate-auditor-basedb.conf | 24+++++++++++++++++++-----
Msrc/exchange/Makefile.am | 3++-
Msrc/exchange/taler-exchange-httpd.c | 74+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msrc/exchange/taler-exchange-httpd_aml-decisions-get.c | 22+++++++++++++---------
Asrc/exchange/taler-exchange-httpd_spa.c | 362+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/exchange/taler-exchange-httpd_spa.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Msrc/exchangedb/exchange_do_insert_aml_decision.sql | 16++++++++++------
Msrc/exchangedb/pg_trigger_aml_process.c | 8++++----
Msrc/include/taler_crypto_lib.h | 96++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msrc/include/taler_util.h | 6------
Msrc/json/json_helper.c | 2--
Msrc/kyclogic/plugin_kyclogic_kycaid.c | 6------
Msrc/lib/exchange_api_stefan.c | 28++++++++++++++++++----------
Msrc/templating/templating_api.c | 5++---
Msrc/testing/taler-unified-setup.sh | 18++++++++++++------
Msrc/util/config.c | 7-------
Msrc/util/currencies.conf | 9---------
Msrc/util/paths.conf | 6+++---
26 files changed, 737 insertions(+), 231 deletions(-)

diff --git a/.gitmodules b/.gitmodules @@ -8,3 +8,7 @@ [submodule "contrib/gana"] path = contrib/gana url = https://git.gnunet.org/gana.git +[submodule "contrib/wallet-core"] + path = contrib/wallet-core + url = https://git.taler.net/wallet-core.git + branch = prebuilt diff --git a/bootstrap b/bootstrap @@ -39,4 +39,17 @@ else echo "Uncrustify not detected, hook not installed. Please install uncrustify if you plan on doing development" fi + +# Generate Makefile.am in contrib/ +cd contrib +rm -f Makefile.am +find wallet-core/aml-backoffice/ -type f -printf ' %p \\\n' | sort > Makefile.am.ext +# Remove extra '\' at the end of the file +truncate -s -2 Makefile.am.ext +cat Makefile.am.in Makefile.am.ext >> Makefile.am +# Prevent accidental editing of the generated Makefile.am +chmod -w Makefile.am +cd .. + +echo "$0: Running autoreconf" autoreconf -fi diff --git a/configure.ac b/configure.ac @@ -128,6 +128,42 @@ fd = epoll_create1(EPOLL_CLOEXEC);]])], AC_DEFINE([[HAVE_EPOLL_CREATE1]], [[1]], [Define if you have epoll_create1 function.])])) +# check for libmicrohttpd +AC_MSG_CHECKING([for microhttpd]) +AC_ARG_WITH([microhttpd], + [AS_HELP_STRING([--with-microhttpd=PFX], [base of microhttpd installation])], + [AC_MSG_RESULT([given as $with_microhttpd])], + [AC_MSG_RESULT([not given]) + with_microhttpd=yes]) +AS_CASE([$with_microhttpd], + [yes], [], + [no], [AC_MSG_ERROR([--with-microhttpd is required])], + [LDFLAGS="-L$with_microhttpd/lib $LDFLAGS" + CPPFLAGS="-I$with_microhttpd/include $CPPFLAGS"]) +MHD_VERSION_AT_LEAST([0.9.71]) + +# check for libjansson (Jansson JSON library) +jansson=0 +AC_MSG_CHECKING([for jansson]) +AC_ARG_WITH([jansson], + [AS_HELP_STRING([--with-jansson=PFX], [base of jansson installation])], + [AC_MSG_RESULT([given as $with_jansson])], + [AC_MSG_RESULT([not given]) + with_jansson=yes]) +AS_CASE([$with_jansson], + [yes], [], + [no], [AC_MSG_ERROR([--with-jansson is required])], + [LDFLAGS="-L$with_jansson/lib $LDFLAGS" + CPPFLAGS="-I$with_jansson/include $CPPFLAGS"]) +AC_CHECK_LIB(jansson,json_dumpb, + [AC_CHECK_HEADER([jansson.h],[jansson=1])]) +AS_IF([test $jansson = 0], + [AC_MSG_ERROR([[ +*** +*** You need libjansson >= 2.10 to build this program. +*** ]])]) + + # Check for GNUnet's libgnunetutil. libgnunetutil=0 AC_MSG_CHECKING([for libgnunetutil]) @@ -295,42 +331,6 @@ AC_CHECK_HEADERS([gnunet/gnunet_sq_lib.h], [AC_CHECK_LIB([gnunetsq], [GNUNET_SQ_result_spec_string], libgnunetsq=1)]) -# check for libmicrohttpd -AC_MSG_CHECKING([for microhttpd]) -AC_ARG_WITH([microhttpd], - [AS_HELP_STRING([--with-microhttpd=PFX], [base of microhttpd installation])], - [AC_MSG_RESULT([given as $with_microhttpd])], - [AC_MSG_RESULT([not given]) - with_microhttpd=yes]) -AS_CASE([$with_microhttpd], - [yes], [], - [no], [AC_MSG_ERROR([--with-microhttpd is required])], - [LDFLAGS="-L$with_microhttpd/lib $LDFLAGS" - CPPFLAGS="-I$with_microhttpd/include $CPPFLAGS"]) -MHD_VERSION_AT_LEAST([0.9.71]) - -# check for libjansson (Jansson JSON library) -jansson=0 -AC_MSG_CHECKING([for jansson]) -AC_ARG_WITH([jansson], - [AS_HELP_STRING([--with-jansson=PFX], [base of jansson installation])], - [AC_MSG_RESULT([given as $with_jansson])], - [AC_MSG_RESULT([not given]) - with_jansson=yes]) -AS_CASE([$with_jansson], - [yes], [], - [no], [AC_MSG_ERROR([--with-jansson is required])], - [LDFLAGS="-L$with_jansson/lib $LDFLAGS" - CPPFLAGS="-I$with_jansson/include $CPPFLAGS"]) -AC_CHECK_LIB(jansson,json_dumpb, - [AC_CHECK_HEADER([jansson.h],[jansson=1])]) -AS_IF([test $jansson = 0], - [AC_MSG_ERROR([[ -*** -*** You need libjansson >= 2.10 to build this program. -*** ]])]) - - CFLAGS=$CFLAGS_SAVE LDFLAGS=$LDFLAGS_SAVE diff --git a/contrib/.gitignore b/contrib/.gitignore @@ -1,2 +1,4 @@ taler-terms-generator locale/**/*.pot +Makefile.am +Makefile.am.ext diff --git a/contrib/Makefile.am b/contrib/Makefile.am @@ -1,63 +0,0 @@ -# This file is in the public domain. - -SUBDIRS = . - -tmplpkgdatadir = $(datadir)/taler/exchange/templates/ -dist_tmplpkgdata_DATA = \ - persona-exchange-unauthorized.en.must \ - persona-load-failure.en.must \ - persona-exchange-unpaid.en.must \ - persona-logic-failure.en.must \ - persona-invalid-response.en.must \ - persona-network-timeout.en.must \ - persona-kyc-failed.en.must \ - persona-provider-failure.en.must - -termsdir=$(datadir)/taler/terms/ -terms_DATA = \ - exchange-tos-v0.rst \ - exchange-tos-bfh-v0.rst \ - exchange-pp-v0.rst - -install-exec-local: - find locale/ -name "*.po" - mkdir -p $(DESTDIR)$(datadir) - cp --parents -r $$(find locale/ -name "*.po") $(DESTDIR)$(datadir) - -rdatadir=$(datadir)/taler/exchange -rdata_DATA = \ - auditor-report.tex.j2 - -bin_SCRIPTS = \ - taler-auditor-dbconfig \ - taler-exchange-dbconfig \ - taler-terms-generator \ - taler-bank-manage-testing \ - taler-nexus-prepare - -edit_script = $(SED) -e 's,%termsdir%,$(termsdir),'g -e 's,%localedir%,$(localedir),'g $(NULL) -taler-terms-generator: taler-terms-generator.in - rm -f $@ $@.tmp && \ - $(edit_script) $< >$@.tmp && \ - chmod a-w+x $@.tmp && \ - mv $@.tmp $@ - -CLEANFILES = \ - taler-terms-generator - -EXTRA_DIST = \ - locale/de/LC_MESSAGES/exchange-tos-v0.po \ - taler-bank-manage-testing \ - taler-nexus-prepare \ - taler-terms-generator.in \ - taler-auditor-dbconfig \ - taler-exchange-dbconfig \ - gana-generate.sh \ - gana/gnu-taler-error-codes/registry.rec \ - gana/gnu-taler-error-codes/Makefile \ - $(terms_DATA) \ - $(rdata_DATA) \ - coverage.sh \ - gnunet.tag \ - microhttpd.tag \ - packages diff --git a/contrib/Makefile.am.in b/contrib/Makefile.am.in @@ -0,0 +1,71 @@ +# This file is in the public domain. + +SUBDIRS = . + +tmplpkgdatadir = $(datadir)/taler/exchange/templates/ +dist_tmplpkgdata_DATA = \ + persona-exchange-unauthorized.en.must \ + persona-load-failure.en.must \ + persona-exchange-unpaid.en.must \ + persona-logic-failure.en.must \ + persona-invalid-response.en.must \ + persona-network-timeout.en.must \ + persona-kyc-failed.en.must \ + persona-provider-failure.en.must + +termsdir=$(datadir)/taler/terms/ +terms_DATA = \ + exchange-tos-v0.rst \ + exchange-tos-bfh-v0.rst \ + exchange-pp-v0.rst + +install-exec-local: + find locale/ -name "*.po" + mkdir -p $(DESTDIR)$(datadir) + cp --parents -r $$(find locale/ -name "*.po") $(DESTDIR)$(datadir) + +rdatadir=$(datadir)/taler/exchange +rdata_DATA = \ + auditor-report.tex.j2 + +bin_SCRIPTS = \ + taler-auditor-dbconfig \ + taler-exchange-dbconfig \ + taler-terms-generator \ + taler-bank-manage-testing \ + taler-nexus-prepare + +edit_script = $(SED) -e 's,%termsdir%,$(termsdir),'g -e 's,%localedir%,$(localedir),'g $(NULL) +taler-terms-generator: taler-terms-generator.in + rm -f $@ $@.tmp && \ + $(edit_script) $< >$@.tmp && \ + chmod a-w+x $@.tmp && \ + mv $@.tmp $@ + +CLEANFILES = \ + taler-terms-generator + +EXTRA_DIST = \ + locale/de/LC_MESSAGES/exchange-tos-v0.po \ + taler-bank-manage-testing \ + taler-nexus-prepare \ + taler-terms-generator.in \ + taler-auditor-dbconfig \ + taler-exchange-dbconfig \ + gana-generate.sh \ + gana/gnu-taler-error-codes/registry.rec \ + gana/gnu-taler-error-codes/Makefile \ + $(terms_DATA) \ + $(rdata_DATA) \ + coverage.sh \ + gnunet.tag \ + microhttpd.tag \ + packages + +spapkgdatadir = $(prefix)/share/taler/exchange/spa/ + +# This is for the single-page-app imported from the wallet-core.git +# prebuilt branch. This MUST be the last line in the +# Makefile.am.in, as it will be combined with the +# actual SPA data by 'bootstrap'! +dist_spapkgdata_DATA = \ diff --git a/contrib/wallet-core b/contrib/wallet-core @@ -0,0 +1 @@ +Subproject commit 621dad2c2ec9a2adc52076cebf65891d6764c802 diff --git a/debian/taler-exchange.install b/debian/taler-exchange.install @@ -29,6 +29,7 @@ usr/share/info/taler-bank* usr/share/info/taler-exchange* usr/share/taler/config.d/* usr/share/taler/exchange/templates/*.must +usr/share/taler/exchange/spa/* # configuration files in /etc/taler debian/etc-taler-exchange/* etc/ diff --git a/src/auditor/generate-auditor-basedb.conf b/src/auditor/generate-auditor-basedb.conf @@ -28,12 +28,26 @@ WIRE_GATEWAY_AUTH_METHOD = basic USERNAME = exchange PASSWORD = x +[exchange-account-2] +PAYTO_URI = "payto://x-taler-bank/localhost/2?receiver-name=2" +ENABLE_DEBIT = YES +ENABLE_CREDIT = YES + +[exchange-accountcredentials-2] +WIRE_GATEWAY_AUTH_METHOD = basic +USERNAME = Exchange +PASSWORD = x +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + +[admin-accountcredentials-2] +WIRE_GATEWAY_AUTH_METHOD = basic +# For now, fakebank still checks against the Exchange account... +USERNAME = Exchange +PASSWORD = x +WIRE_GATEWAY_URL = "http://localhost:8082/accounts/2/taler-wire-gateway/" + + [merchant] -WIREFORMAT = default -DEFAULT_MAX_DEPOSIT_FEE = TESTKUDOS:0.1 -KEYFILE = ${TALER_DATA_HOME}/merchant/merchant.priv -DEFAULT_MAX_WIRE_FEE = TESTKUDOS:0.10 -WIRE_TRANSFER_DELAY = 1 minute FORCE_AUDIT = YES [merchantdb-postgres] diff --git a/src/exchange/Makefile.am b/src/exchange/Makefile.am @@ -181,8 +181,9 @@ taler_exchange_httpd_SOURCES = \ taler-exchange-httpd_reserves_open.c taler-exchange-httpd_reserves_open.h \ taler-exchange-httpd_reserves_purse.c taler-exchange-httpd_reserves_purse.h \ taler-exchange-httpd_responses.c taler-exchange-httpd_responses.h \ + taler-exchange-httpd_spa.c taler-exchange-httpd_spa.h \ taler-exchange-httpd_terms.c taler-exchange-httpd_terms.h \ - taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h + taler-exchange-httpd_transfers_get.c taler-exchange-httpd_transfers_get.h taler_exchange_httpd_LDADD = \ $(LIBGCRYPT_LIBS) \ diff --git a/src/exchange/taler-exchange-httpd.c b/src/exchange/taler-exchange-httpd.c @@ -68,6 +68,7 @@ #include "taler-exchange-httpd_reserves_history.h" #include "taler-exchange-httpd_reserves_open.h" #include "taler-exchange-httpd_reserves_purse.h" +#include "taler-exchange-httpd_spa.h" #include "taler-exchange-httpd_terms.h" #include "taler-exchange-httpd_transfers_get.h" #include "taler_exchangedb_lib.h" @@ -1508,6 +1509,56 @@ handle_post_auditors (struct TEH_RequestContext *rc, /** + * Generates the response for "/", redirecting the + * client to the "/webui/" from where we serve the SPA. + * + * @param rc request context + * @param args remaining arguments (should be empty) + * @return MHD result code + */ +static MHD_RESULT +spa_redirect (struct TEH_RequestContext *rc, + const char *const args[]) +{ + const char *text = "Redirecting to /webui/"; + struct MHD_Response *response; + + response = MHD_create_response_from_buffer (strlen (text), + (void *) text, + MHD_RESPMEM_PERSISTENT); + if (NULL == response) + { + GNUNET_break (0); + return MHD_NO; + } + TALER_MHD_add_global_headers (response); + GNUNET_break (MHD_YES == + MHD_add_response_header (response, + MHD_HTTP_HEADER_CONTENT_TYPE, + "text/plain")); + if (MHD_NO == + MHD_add_response_header (response, + MHD_HTTP_HEADER_LOCATION, + "/webui/")) + { + GNUNET_break (0); + MHD_destroy_response (response); + return MHD_NO; + } + + { + MHD_RESULT ret; + + ret = MHD_queue_response (rc->connection, + MHD_HTTP_FOUND, + response); + MHD_destroy_response (response); + return ret; + } +} + + +/** * Handle incoming HTTP request. * * @param cls closure for MHD daemon (unused) @@ -1540,15 +1591,11 @@ handle_mhd_request (void *cls, .data = "User-agent: *\nDisallow: /\n", .response_code = MHD_HTTP_OK }, - /* Landing page, tell humans to go away. */ + /* Landing page, redirect to SPA */ { .url = "", .method = MHD_HTTP_METHOD_GET, - .handler.get = TEH_handler_static_response, - .mime_type = "text/plain", - .data = - "Hello, I'm the Taler exchange. This HTTP server is not for humans.\n", - .response_code = MHD_HTTP_OK + .handler.get = &spa_redirect }, /* AGPL licensing page, redirect to source. As per the AGPL-license, every deployment is required to offer the user a download of the source of @@ -1778,7 +1825,13 @@ handle_mhd_request (void *cls, .handler.post = &handle_post_aml, .nargs = 2 }, - + { + .url = "webui", + .method = MHD_HTTP_METHOD_GET, + .handler.get = &TEH_handler_spa, + .nargs = 1, + .nargs_is_upper_bound = true + }, /* mark end of list */ { @@ -2543,6 +2596,13 @@ run (void *cls, return; } if (GNUNET_OK != + TEH_spa_init ()) + { + global_ret = EXIT_NOTCONFIGURED; + GNUNET_SCHEDULER_shutdown (); + return; + } + if (GNUNET_OK != TALER_TEMPLATING_init ("exchange")) { global_ret = EXIT_FAILURE; diff --git a/src/exchange/taler-exchange-httpd_aml-decisions-get.c b/src/exchange/taler-exchange-httpd_aml-decisions-get.c @@ -81,7 +81,7 @@ TEH_handler_aml_decisions_get ( { enum TALER_AmlDecisionState decision; int delta = -20; - unsigned long long start = INT64_MAX; + unsigned long long start; const char *state_str = args[0]; if (NULL == state_str) @@ -123,40 +123,44 @@ TEH_handler_aml_decisions_get ( p = MHD_lookup_connection_value (rc->connection, MHD_GET_ARGUMENT_KIND, - "start"); + "delta"); if (NULL != p) { char dummy; if (1 != sscanf (p, - "%llu%c", - &start, + "%d%c", + &delta, &dummy)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - "start"); + "delta"); } } + if (delta > 0) + start = 0; + else + start = INT64_MAX; p = MHD_lookup_connection_value (rc->connection, MHD_GET_ARGUMENT_KIND, - "delta"); + "start"); if (NULL != p) { char dummy; if (1 != sscanf (p, - "%d%c", - &delta, + "%llu%c", + &start, &dummy)) { GNUNET_break_op (0); return TALER_MHD_reply_with_error (rc->connection, MHD_HTTP_BAD_REQUEST, TALER_EC_GENERIC_PARAMETER_MALFORMED, - "delta"); + "start"); } } } diff --git a/src/exchange/taler-exchange-httpd_spa.c b/src/exchange/taler-exchange-httpd_spa.c @@ -0,0 +1,362 @@ +/* + This file is part of TALER + Copyright (C) 2020, 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 EXCHANGEABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-exchange-httpd_spa.c + * @brief logic to load the single page app (/) + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include "taler_util.h" +#include "taler_mhd_lib.h" +#include <gnunet/gnunet_mhd_compat.h> +#include "taler-exchange-httpd.h" + + +/** + * Resource from the WebUi. + */ +struct WebuiFile +{ + /** + * Kept in a DLL. + */ + struct WebuiFile *next; + + /** + * Kept in a DLL. + */ + struct WebuiFile *prev; + + /** + * Path this resource matches. + */ + char *path; + + /** + * SPA resource, compressed. + */ + struct MHD_Response *zspa; + + /** + * SPA resource, vanilla. + */ + struct MHD_Response *spa; + +}; + + +/** + * Resources of the WebuUI, kept in a DLL. + */ +static struct WebuiFile *webui_head; + +/** + * Resources of the WebuUI, kept in a DLL. + */ +static struct WebuiFile *webui_tail; + + +MHD_RESULT +TEH_handler_spa (struct TEH_RequestContext *rc, + const char *const args[]) +{ + struct WebuiFile *w = NULL; + const char *infix = args[0]; + + if ( (NULL == infix) || + (0 == strcmp (infix, + "")) ) + infix = "index.html"; + for (struct WebuiFile *pos = webui_head; + NULL != pos; + pos = pos->next) + if (0 == strcmp (infix, + pos->path)) + { + w = pos; + break; + } + if (NULL == w) + return TALER_MHD_reply_with_error (rc->connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_GENERIC_ENDPOINT_UNKNOWN, + rc->url); + if ( (MHD_YES == + TALER_MHD_can_compress (rc->connection)) && + (NULL != w->zspa) ) + return MHD_queue_response (rc->connection, + MHD_HTTP_OK, + w->zspa); + return MHD_queue_response (rc->connection, + MHD_HTTP_OK, + w->spa); +} + + +/** + * Function called on each file to load for the WebUI. + * + * @param cls NULL + * @param dn name of the file to load + */ +static enum GNUNET_GenericReturnValue +build_webui (void *cls, + const char *dn) +{ + static struct + { + const char *ext; + const char *mime; + } mime_map[] = { + { + .ext = "css", + .mime = "text/css" + }, + { + .ext = "html", + .mime = "text/html" + }, + { + .ext = "js", + .mime = "text/javascript" + }, + { + .ext = "jpg", + .mime = "image/jpeg" + }, + { + .ext = "jpeg", + .mime = "image/jpeg" + }, + { + .ext = "png", + .mime = "image/png" + }, + { + .ext = "svg", + .mime = "image/svg+xml" + }, + { + .ext = NULL, + .mime = NULL + }, + }; + int fd; + struct stat sb; + struct MHD_Response *zspa = NULL; + struct MHD_Response *spa; + const char *ext; + const char *mime; + + (void) cls; + /* finally open template */ + fd = open (dn, + O_RDONLY); + if (-1 == fd) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + dn); + return GNUNET_SYSERR; + } + if (0 != + fstat (fd, + &sb)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + dn); + GNUNET_break (0 == close (fd)); + return GNUNET_SYSERR; + } + + mime = NULL; + ext = strrchr (dn, '.'); + if (NULL == ext) + { + GNUNET_break (0 == close (fd)); + return GNUNET_OK; + } + ext++; + for (unsigned int i = 0; NULL != mime_map[i].ext; i++) + if (0 == strcasecmp (ext, + mime_map[i].ext)) + { + mime = mime_map[i].mime; + break; + } + + { + void *in; + ssize_t r; + size_t csize; + + in = GNUNET_malloc_large (sb.st_size); + if (NULL == in) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "malloc"); + GNUNET_break (0 == close (fd)); + return GNUNET_SYSERR; + } + r = read (fd, + in, + sb.st_size); + if ( (-1 == r) || + (sb.st_size != (size_t) r) ) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "read", + dn); + GNUNET_free (in); + GNUNET_break (0 == close (fd)); + return GNUNET_SYSERR; + } + csize = (size_t) r; + if (MHD_YES == + TALER_MHD_body_compress (&in, + &csize)) + { + zspa = MHD_create_response_from_buffer (csize, + in, + MHD_RESPMEM_MUST_FREE); + if (NULL != zspa) + { + if (MHD_NO == + MHD_add_response_header (zspa, + MHD_HTTP_HEADER_CONTENT_ENCODING, + "deflate")) + { + GNUNET_break (0); + MHD_destroy_response (zspa); + zspa = NULL; + } + if (NULL != mime) + GNUNET_break (MHD_YES == + MHD_add_response_header (zspa, + MHD_HTTP_HEADER_CONTENT_TYPE, + mime)); + } + } + else + { + GNUNET_free (in); + } + } + + spa = MHD_create_response_from_fd (sb.st_size, + fd); + if (NULL == spa) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + dn); + GNUNET_break (0 == close (fd)); + if (NULL != zspa) + { + MHD_destroy_response (zspa); + zspa = NULL; + } + return GNUNET_SYSERR; + } + if (NULL != mime) + GNUNET_break (MHD_YES == + MHD_add_response_header (spa, + MHD_HTTP_HEADER_CONTENT_TYPE, + mime)); + + { + struct WebuiFile *w; + const char *fn; + + fn = strrchr (dn, '/'); + GNUNET_assert (NULL != fn); + w = GNUNET_new (struct WebuiFile); + w->path = GNUNET_strdup (fn + 1); + w->spa = spa; + w->zspa = zspa; + GNUNET_CONTAINER_DLL_insert (webui_head, + webui_tail, + w); + } + return GNUNET_OK; +} + + +enum GNUNET_GenericReturnValue +TEH_spa_init () +{ + char *dn; + + { + char *path; + + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + if (NULL == path) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_asprintf (&dn, + "%sexchange/spa/", + path); + GNUNET_free (path); + } + + if (-1 == + GNUNET_DISK_directory_scan (dn, + &build_webui, + NULL)) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load WebUI from `%s'\n", + dn); + GNUNET_free (dn); + return GNUNET_SYSERR; + } + GNUNET_free (dn); + return GNUNET_OK; +} + + +/** + * Nicely shut down. + */ +void __attribute__ ((destructor)) +get_spa_fini () +{ + struct WebuiFile *w; + + while (NULL != (w = webui_head)) + { + GNUNET_CONTAINER_DLL_remove (webui_head, + webui_tail, + w); + if (NULL != w->spa) + { + MHD_destroy_response (w->spa); + w->spa = NULL; + } + if (NULL != w->zspa) + { + MHD_destroy_response (w->zspa); + w->zspa = NULL; + } + GNUNET_free (w->path); + GNUNET_free (w); + } +} diff --git a/src/exchange/taler-exchange-httpd_spa.h b/src/exchange/taler-exchange-httpd_spa.h @@ -0,0 +1,49 @@ +/* + This file is part of TALER + Copyright (C) 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 EXCHANGEABILITY or FITNESS FOR + A PARTICULAR PURPOSE. See the GNU General Public License for more details. + + You should have received a copy of the GNU General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +*/ +/** + * @file taler-exchange-httpd_spa.h + * @brief logic to preload and serve static files + * @author Christian Grothoff + */ +#ifndef TALER_EXCHANGE_HTTPD_SPA_H +#define TALER_EXCHANGE_HTTPD_SPA_H + +#include <microhttpd.h> +#include "taler-exchange-httpd.h" + + +/** + * Return our single-page-app user interface (see contrib/wallet-core/). + * + * @param rc context of the handler + * @param[in,out] args remaining arguments (ignored) + * @return #MHD_YES on success (reply queued), #MHD_NO on error (close connection) + */ +MHD_RESULT +TEH_handler_spa (struct TEH_RequestContext *rc, + const char *const args[]); + + +/** + * Preload and compress SPA files. + * + * @return #GNUNET_OK on success + */ +enum GNUNET_GenericReturnValue +TEH_spa_init (void); + + +#endif diff --git a/src/exchangedb/exchange_do_insert_aml_decision.sql b/src/exchangedb/exchange_do_insert_aml_decision.sql @@ -32,7 +32,7 @@ AS $$ BEGIN -- Check officer is eligible to make decisions. PERFORM - FROM exchange.aml_staff + FROM aml_staff WHERE decider_pub=in_decider_pub AND is_active AND NOT read_only; @@ -47,7 +47,7 @@ out_invalid_officer=FALSE; -- Check no more recent decision exists. SELECT decision_time INTO out_last_date - FROM exchange.aml_history + FROM aml_history WHERE h_payto=in_h_payto ORDER BY decision_time DESC; IF FOUND @@ -57,7 +57,7 @@ THEN -- Refuse to insert older decision. RETURN; END IF; - UPDATE exchange.aml_status + UPDATE aml_status SET threshold=in_new_threshold ,status=in_new_status ,kyc_requirement=in_requirement_row @@ -65,7 +65,7 @@ THEN ASSERT FOUND, 'cannot have AML decision history but no AML status'; ELSE out_last_date = 0; - INSERT INTO exchange.aml_status + INSERT INTO aml_status (h_payto ,threshold ,status @@ -74,11 +74,15 @@ ELSE (in_h_payto ,in_new_threshold ,in_new_status - ,in_requirement_row); + ,in_requirement_row) + ON CONFLICT (h_payto) DO + UPDATE SET + threshold=in_new_threshold + ,status=in_new_status; END IF; -INSERT INTO exchange.aml_history +INSERT INTO aml_history (h_payto ,new_threshold ,new_status diff --git a/src/exchangedb/pg_trigger_aml_process.c b/src/exchangedb/pg_trigger_aml_process.c @@ -46,12 +46,12 @@ TEH_PG_trigger_aml_process ( "(h_payto" ",threshold" ",status)" - "VALUES" - "($1, $2, 1)" // 1: decision needed - "ON CONFLICT DO" + " VALUES" + " ($1, $2, 1)" // 1: decision needed + " ON CONFLICT (h_payto) DO" " UPDATE SET" " threshold=$2" - " ,status=status | 1;"); // do not clear 'frozen' status + " ,status=aml_status.status | 1;"); // do not clear 'frozen' status return GNUNET_PQ_eval_prepared_non_select (pg->conn, "trigger_aml_process", params); diff --git a/src/include/taler_crypto_lib.h b/src/include/taler_crypto_lib.h @@ -50,6 +50,54 @@ #define TALER_CNC_KAPPA_MINUS_ONE_STR "2" +/** + * Possible AML decision states. + */ +enum TALER_AmlDecisionState +{ + + /** + * All AML requirements are currently satisfied. + */ + TALER_AML_NORMAL = 0, + + /** + * An AML investigation is pending. + */ + TALER_AML_PENDING = 1, + + /** + * An AML decision has concluded that the funds must be frozen. + */ + TALER_AML_FROZEN = 2 + +}; + + +/** + * Possible algorithms for confirmation code generation. + */ +enum TALER_MerchantConfirmationAlgorithm +{ + + /** + * No purchase confirmation. + */ + TALER_MCA_NONE = 0, + + /** + * Purchase confirmation without payment + */ + TALER_MCA_WITHOUT_PRICE = 1, + + /** + * Purchase confirmation with payment + */ + TALER_MCA_WITH_PRICE = 2 + +}; + + /* ****************** Coin crypto primitives ************* */ GNUNET_NETWORK_STRUCT_BEGIN @@ -562,54 +610,6 @@ struct TALER_AmlOfficerSignatureP /** - * Possible AML decision states. - */ -enum TALER_AmlDecisionState -{ - - /** - * All AML requirements are currently satisfied. - */ - TALER_AML_NORMAL = 0, - - /** - * An AML investigation is pending. - */ - TALER_AML_PENDING = 1, - - /** - * An AML decision has concluded that the funds must be frozen. - */ - TALER_AML_FROZEN = 2 - -}; - - -/** - * Possible algorithms for confirmation code generation. - */ -enum TALER_MerchantConfirmationAlgorithm -{ - - /** - * No purchase confirmation. - */ - TALER_MCA_NONE = 0, - - /** - * Purchase confirmation without payment - */ - TALER_MCA_WITHOUT_PRICE = 1, - - /** - * Purchase confirmation with payment - */ - TALER_MCA_WITH_PRICE = 2 - -}; - - -/** * Commitment value for the refresh protocol. * See #TALER_refresh_get_commitment(). */ diff --git a/src/include/taler_util.h b/src/include/taler_util.h @@ -242,12 +242,6 @@ struct TALER_CurrencySpecification unsigned int num_fractional_trailing_zero_digits; /** - * True to put the currency symbol before the number, - * false to put the currency symbol after the number. - */ - bool is_currency_name_leading; - - /** * Mapping of powers of 10 to alternative currency names or symbols. * Keys are the decimal powers, values the currency symbol to use. * Map MUST contain an entry for "0" to the default currency symbol. diff --git a/src/json/json_helper.c b/src/json/json_helper.c @@ -179,8 +179,6 @@ parse_cspec (void *cls, &fnd), GNUNET_JSON_spec_uint32 ("num_fractional_trailing_zero_digits", &ftzd), - GNUNET_JSON_spec_bool ("is_currency_name_leading", - &r_cspec->is_currency_name_leading), GNUNET_JSON_spec_object_const ("alt_unit_names", &map), GNUNET_JSON_spec_end () diff --git a/src/kyclogic/plugin_kyclogic_kycaid.c b/src/kyclogic/plugin_kyclogic_kycaid.c @@ -917,12 +917,6 @@ handle_webhook_finished (void *cls, GNUNET_log (GNUNET_ERROR_TYPE_INFO, "Webhook returned with HTTP status %u\n", (unsigned int) response_code); -#if 1 - if (NULL != j) - json_dumpf (j, - stderr, - JSON_INDENT (2)); -#endif wh->kycaid_response_code = response_code; wh->json_response = json_incref ((json_t *) j); switch (response_code) diff --git a/src/lib/exchange_api_stefan.c b/src/lib/exchange_api_stefan.c @@ -299,22 +299,30 @@ TALER_EXCHANGE_keys_stefan_round ( struct TALER_Amount *val) { const struct TALER_Amount *min; - uint32_t mod = 1; + uint32_t mod; uint32_t frac; - uint32_t rst; + uint32_t lim; + if (0 == val->fraction) + { + /* rounding of non-fractions not supported */ + return; + } min = get_unit (keys); if (NULL == min) return; - frac = min->fraction; - while (0 == frac % 10) + if (0 == min->fraction) + { + frac = TALER_AMOUNT_FRAC_BASE; + } + else { - mod *= 10; - frac /= 10; + frac = min->fraction; } - rst = val->fraction % mod; - if (rst < mod / 2) - val->fraction -= rst; + lim = frac / 2; + mod = val->fraction % frac; + if (mod < lim) + val->fraction -= mod; /* round down */ else - val->fraction += mod - rst; + val->fraction += frac - mod; /* round up */ } diff --git a/src/templating/templating_api.c b/src/templating/templating_api.c @@ -100,7 +100,7 @@ lookup_template (struct MHD_Connection *connection, if (NULL == best) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "No templates found in `%s'\n", + "No templates found for `%s'\n", name); return NULL; } @@ -360,7 +360,6 @@ load_template (void *cls, (void) cls; if ('.' == filename[0]) return GNUNET_OK; - name = strrchr (filename, '/'); if (NULL == name) @@ -395,7 +394,7 @@ load_template (void *cls, &sb)) { GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, - "open", + "fstat", filename); GNUNET_break (0 == close (fd)); return GNUNET_OK; diff --git a/src/testing/taler-unified-setup.sh b/src/testing/taler-unified-setup.sh @@ -260,9 +260,13 @@ register_bank_account() { } register_fakebank_account() { - # FIXME: may need to pass 'is_taler_exchange' here - # in the future to get exchange facade from libeufin! - BODY='{"username":"'"$1"'","password":"'"$2"'","name":"'"$1"'"}' + if [ "$1" = "exchange" ] || [ "$1" = "Exchange" ] + then + IS_EXCHANGE="true" + else + IS_EXCHANGE="false" + fi + BODY='{"username":"'"$1"'","password":"'"$2"'","name":"'"$1"'","is_taler_exchange":'"$IS_EXCHANGE"'}' wget \ --post-data="$BODY" \ --header='Content-type: application/json' \ @@ -270,6 +274,7 @@ register_fakebank_account() { --waitretry=1 \ --timeout=30 \ "http://localhost:$BANK_PORT/accounts" \ + -a wget-register-account.log \ -o /dev/null \ -O /dev/null \ >/dev/null @@ -705,18 +710,19 @@ then sleep "$DEFAULT_SLEEP" wget \ --tries=1 \ - --timeout=1 \ + --timeout=5 \ "${EXCHANGE_URL}keys" \ + -a wget-keys-check.log \ -o /dev/null \ -O "$LAST_RESPONSE" \ - >/dev/null || continue + >/dev/null || continue OK="1" break done if [ "1" != "$OK" ] then cat "$LAST_RESPONSE" - exit_fail " Failed to setup keys" + exit_fail " Failed to fetch ${EXCHANGE_URL}keys" fi rm "$LAST_RESPONSE" echo " OK" diff --git a/src/util/config.c b/src/util/config.c @@ -233,11 +233,6 @@ parse_currencies_cb (void *cls, cpc->len_cspecs * 2 + 4); } cspec = &cpc->cspecs[cpc->num_currencies++]; - cspec->is_currency_name_leading - = GNUNET_CONFIGURATION_get_value_yesno (cpc->cfg, - section, - "NAME_LEADING"); - if (GNUNET_OK != GNUNET_CONFIGURATION_get_value_string (cpc->cfg, section, @@ -483,8 +478,6 @@ TALER_CONFIG_currency_specs_to_json (const struct cspec->num_fractional_normal_digits), GNUNET_JSON_pack_uint64 ("num_fractional_trailing_zero_digits", cspec->num_fractional_trailing_zero_digits), - GNUNET_JSON_pack_bool ("is_currency_name_leading", - cspec->is_currency_name_leading), GNUNET_JSON_pack_object_incref ("alt_unit_names", cspec->map_alt_unit_names)); } diff --git a/src/util/currencies.conf b/src/util/currencies.conf @@ -5,7 +5,6 @@ code = "EUR" fractional_input_digits = 2 fractional_normal_digits = 2 fractional_trailing_zero_digits = 2 -is_currency_name_leading = NO alt_unit_names = {"0":"€"} [currency-swiss-francs] @@ -15,7 +14,6 @@ code = "CHF" fractional_input_digits = 2 fractional_normal_digits = 2 fractional_trailing_zero_digits = 2 -is_currency_name_leading = YES alt_unit_names = {"0":"Fr.","-2":"Rp."} [currency-forint] @@ -25,7 +23,6 @@ code = "HUF" fractional_input_digits = 0 fractional_normal_digits = 0 fractional_trailing_zero_digits = 0 -is_currency_name_leading = NO alt_unit_names = {"0":"Ft"} [currency-us-dollar] @@ -35,7 +32,6 @@ code = "USD" fractional_input_digits = 2 fractional_normal_digits = 2 fractional_trailing_zero_digits = 2 -is_currency_name_leading = YES alt_unit_names = {"0":"$"} [currency-kudos] @@ -45,7 +41,6 @@ code = "KUDOS" fractional_input_digits = 2 fractional_normal_digits = 2 fractional_trailing_zero_digits = 2 -is_currency_name_leading = NO alt_unit_names = {"0":"ク"} [currency-testkudos] @@ -55,7 +50,6 @@ code = "TESTKUDOS" fractional_input_digits = 2 fractional_normal_digits = 2 fractional_trailing_zero_digits = 2 -is_currency_name_leading = NO alt_unit_names = {"0":"テ","3":"kテ","-3":"mテ"} [currency-japanese-yen] @@ -65,7 +59,6 @@ code = "JPY" fractional_input_digits = 2 fractional_normal_digits = 0 fractional_trailing_zero_digits = 2 -is_currency_name_leading = YES alt_unit_names = {"0":"¥"} [currency-bitcoin-mainnet] @@ -75,7 +68,6 @@ code = "BITCOINBTC" fractional_input_digits = 8 fractional_normal_digits = 3 fractional_trailing_zero_digits = 0 -is_currency_name_leading = NO alt_unit_names = {"0":"BTC","-3":"mBTC"} [currency-ethereum] @@ -85,5 +77,4 @@ code = "EthereumWAI" fractional_input_digits = 0 fractional_normal_digits = 0 fractional_trailing_zero_digits = 0 -is_currency_name_leading = NO alt_unit_names = {"0":"WAI","3":"KWAI","6":"MWAI","9":"GWAI","12":"Szabo","15":"Finney","18":"Ether","21":"KEther","24":"MEther"} diff --git a/src/util/paths.conf b/src/util/paths.conf @@ -17,13 +17,13 @@ TALER_HOME = ${TALER_TEST_HOME:-${HOME:-${USERPROFILE}}} # for how these should be used. # Persistent data storage -TALER_DATA_HOME = ${TALER_TEST_HOME:-${XDG_DATA_HOME:-${TALER_HOME}}}/.local/share/taler/ +TALER_DATA_HOME = ${XDG_DATA_HOME:-${TALER_HOME}/.local/share}/taler/ # Configuration files -TALER_CONFIG_HOME = ${TALER_TEST_HOME:-${XDG_CONFIG_HOME:-${TALER_HOME}}}/.config/taler/ +TALER_CONFIG_HOME = ${XDG_CONFIG_HOME:-${TALER_HOME}/.config}/taler/ # Cached data, no big deal if lost -TALER_CACHE_HOME = ${TALER_TEST_HOME:-${XDG_CACHE_HOME:-${TALER_HOME}}}/.cache/taler/ +TALER_CACHE_HOME = ${XDG_CACHE_HOME:-${TALER_HOME}/.cache}/taler/ # Runtime data (always lost on system boot) TALER_RUNTIME_DIR = ${TMPDIR:-${TMP:-/tmp}}/taler-system-runtime/