diff options
author | Jonathan Buchanan <jonathan.russ.buchanan@gmail.com> | 2020-08-20 00:49:30 -0400 |
---|---|---|
committer | Jonathan Buchanan <jonathan.russ.buchanan@gmail.com> | 2020-08-20 01:01:05 -0400 |
commit | 9fce403bce4fe5f2be507a32ee7b4dfac296c26a (patch) | |
tree | 9c0c4509929e81f5b5eac6aa36e32d479c9643e3 /src | |
parent | 8d172853c1c7b1ca8d9dc31194c4e73e79499a80 (diff) | |
parent | d6f28270094db1ffa4e5526ce556e7fc23e8e9b9 (diff) | |
download | merchant-9fce403bce4fe5f2be507a32ee7b4dfac296c26a.tar.gz merchant-9fce403bce4fe5f2be507a32ee7b4dfac296c26a.tar.bz2 merchant-9fce403bce4fe5f2be507a32ee7b4dfac296c26a.zip |
Merge branch 'master' of ssh://git.taler.net/merchant
Diffstat (limited to 'src')
-rw-r--r-- | src/backend/Makefile.am | 2 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd.c | 9 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_get-orders-ID.c | 51 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_statics.c | 336 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_statics.h | 49 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_templating.c | 11 | ||||
-rw-r--r-- | src/include/taler_merchant_service.h | 2 | ||||
-rw-r--r-- | src/lib/merchant_api_common.c | 11 |
8 files changed, 446 insertions, 25 deletions
diff --git a/src/backend/Makefile.am b/src/backend/Makefile.am index af86034b..60e8125e 100644 --- a/src/backend/Makefile.am +++ b/src/backend/Makefile.am @@ -95,6 +95,8 @@ taler_merchant_httpd_SOURCES = \ taler-merchant-httpd_qr.h \ taler-merchant-httpd_reserves.c \ taler-merchant-httpd_reserves.h \ + taler-merchant-httpd_statics.c \ + taler-merchant-httpd_statics.h \ taler-merchant-httpd_templating.c \ taler-merchant-httpd_templating.h taler_merchant_httpd_LDADD = \ diff --git a/src/backend/taler-merchant-httpd.c b/src/backend/taler-merchant-httpd.c index 54b8d163..5c6924c2 100644 --- a/src/backend/taler-merchant-httpd.c +++ b/src/backend/taler-merchant-httpd.c @@ -63,6 +63,7 @@ #include "taler-merchant-httpd_post-orders-ID-refund.h" #include "taler-merchant-httpd_post-tips-ID-pickup.h" #include "taler-merchant-httpd_reserves.h" +#include "taler-merchant-httpd_statics.h" #include "taler-merchant-httpd_templating.h" /** @@ -1147,6 +1148,13 @@ url_handler (void *cls, to set a conservative bound for sane wallets */ .max_upload = 1024 * 1024 }, + /* GET /static/ *: */ + { + .url_prefix = "/static/", + .method = MHD_HTTP_METHOD_GET, + .have_id_segment = true, + .handler = &TMH_return_static + }, { NULL } @@ -1579,6 +1587,7 @@ run (void *cls, "FORCE_AUDIT")) TMH_force_audit = GNUNET_YES; TMH_templating_init (); + TMH_statics_init (); if (GNUNET_SYSERR == TMH_EXCHANGES_init (config)) { diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c index 027f9f4d..a02c45d5 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -453,6 +453,34 @@ TMH_make_taler_pay_uri (struct MHD_Connection *con, /** + * Return the order summary of the contract of @a god in the + * preferred language of the HTTP client. + * + * @param god order to extract summary from + * @return NULL if no summary was provided in the contract + */ +static const char * +get_order_summary (struct GetOrderData *god) +{ + const char *language_pattern; + const char *ret; + + language_pattern = MHD_lookup_connection_value (god->sc.con, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_ACCEPT_LANGUAGE); + if (NULL == language_pattern) + language_pattern = "en"; + ret = json_string_value (TALER_JSON_extract_i18n (god->contract_terms, + language_pattern, + "summary")); + if (NULL == ret) + GNUNET_log (GNUNET_ERROR_TYPE_WARNING, + "No order summary found!\n"); + return ret; +} + + +/** * The client did not yet pay, send it the payment request. * * @param god check pay request context @@ -512,10 +540,7 @@ send_pay_request (struct GetOrderData *god, { "taler_pay_qrcode_svg", qr }, { "order_summary", - // FIXME #6458: implement logic to extract summary based on - // language preferences from summary_i18n if present. - json_string_value (json_object_get (god->contract_terms, - "summary")) }, + get_order_summary (god) }, { NULL, NULL } }; enum GNUNET_GenericReturnValue res; @@ -848,7 +873,8 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, TALER_EC_GET_ORDERS_DB_LOOKUP_ERROR, "database error looking up order"); } - god->unclaimed = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && !contract_available; + god->unclaimed = (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == qs) && + ! contract_available; if ( (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS == qs) && (NULL == god->contract_terms) ) { @@ -1052,9 +1078,8 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, "Awaiting refund exceeding %s\n", TALER_amount2s (&god->sc.refund_expected)); if (god->sc.awaiting_refund_obtained) - GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, - "Awaiting pending refunds\n", - TALER_amount2s (&god->sc.refund_expected)); + GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, + "Awaiting pending refunds\n"); suspend_god (god); return MHD_YES; } @@ -1087,10 +1112,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, { struct KVC kvc[] = { { "order_summary", - // FIXME #6458: implement logic to extract summary based on - // language preferences from summary_i18n if present. - json_string_value (json_object_get (god->contract_terms, - "summary")) }, + get_order_summary (god) }, { "refund_amount", TALER_amount2s (&god->refund_amount) }, { "taler_refund_uri", @@ -1118,10 +1140,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, { struct KVC kvc[] = { { "order_summary", - // FIXME #6458: implement logic to extract summary based on - // language preferences from summary_i18n if present. - json_string_value (json_object_get (god->contract_terms, - "summary")) }, + get_order_summary (god) }, { "refund_amount", TALER_amount2s (&god->refund_amount) }, { "contract", diff --git a/src/backend/taler-merchant-httpd_statics.c b/src/backend/taler-merchant-httpd_statics.c new file mode 100644 index 00000000..e8be2cba --- /dev/null +++ b/src/backend/taler-merchant-httpd_statics.c @@ -0,0 +1,336 @@ +/* + This file is part of TALER + Copyright (C) 2020 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant/backend/taler-merchant-httpd_statics.c + * @brief logic to load and complete HTML templates + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <taler/taler_util.h> +#include <taler/taler_mhd_lib.h> +#include "taler-merchant-httpd_statics.h" +#include "../mustach/mustach.h" +#include <gnunet/gnunet_mhd_compat.h> + + +/** + * Entry in a key-value array we use to cache templates. + */ +struct TVE +{ + /** + * A name, used as the key. NULL for the last entry. + */ + char *name; + + /** + * Language the template is in. + */ + char *lang; + + /** + * Pre-built reply. + */ + struct MHD_Response *reply; + +}; + + +/** + * Array of templates loaded into RAM. + */ +static struct TVE *loaded; + +/** + * Length of the #loaded array. + */ +static unsigned int loaded_length; + + +/** + * Load Mustach template into memory. Note that we intentionally cache + * failures, that is if we ever failed to load a template, we will never try + * again. + * + * @param connection the connection we act upon + * @param name name of the template file to load + * (MUST be a 'static' string in memory!) + * @return NULL on error, otherwise the template + */ +static const struct TVE * +lookup_file (struct MHD_Connection *connection, + const char *name) +{ + struct TVE *best = NULL; + const char *lang; + + lang = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_ACCEPT_LANGUAGE); + if (NULL == lang) + lang = "en"; + /* find best match by language */ + for (unsigned int i = 0; i<loaded_length; i++) + { + if (0 != strcmp (loaded[i].name, + name)) + continue; /* does not match by name */ + if (NULL == loaded[i].lang) /* no language == always best match */ + return &loaded[i]; + if ( (NULL == best) || + (TALER_language_matches (lang, + loaded[i].lang) > + TALER_language_matches (lang, + best->lang) ) ) + best = &loaded[i]; + } + if (NULL == best) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No static file found for `%s'\n", + name); + return NULL; + } + return best; +} + + +/** + * Check for the existence of a static file and return the result if it + * matches. Otherwise returns a "not found" page. + * + * @param connection the connection we act upon + * @param template basename of the template to load + * @param taler_uri value for "Taler:" header to set, or NULL + * @param kvc key value pairs to fill in + * @return #MHD_YES on success (reply queued), #MHD_NO on error (close connection) + */ +MHD_RESULT +TMH_return_static (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc) +{ + const struct TVE *tmpl; + + tmpl = lookup_file (connection, + hc->infix); + if (NULL == tmpl) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load static file `%s'\n", + hc->infix); + return TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_FOUND, + TALER_EC_ENDPOINT_UNKNOWN, + hc->infix); + } + + return MHD_queue_response (connection, + MHD_HTTP_OK, + tmpl->reply); +} + + +/** + * Function called with a static file's filename. + * + * @param cls closure + * @param filename complete filename (absolute path) + * @return #GNUNET_OK to continue to iterate, + * #GNUNET_NO to stop iteration with no error, + * #GNUNET_SYSERR to abort iteration with error! + */ +static int +load_static_file (void *cls, + const char *filename) +{ + char *lang; + char *end; + int fd; + struct stat sb; + const char *name; + struct MHD_Response *reply; + + if ('.' == filename[0]) + return GNUNET_OK; + name = strrchr (filename, + '/'); + if (NULL == name) + name = filename; + else + name++; + lang = strchr (name, + '.'); + if (NULL == lang) + return GNUNET_OK; /* name must include _some_ extension */ + lang++; + end = strchr (lang, + '.'); + if (NULL == end) + { + /* language was not present, we ONLY have the extension */ + end = lang; + lang = NULL; + } + /* finally open template */ + fd = open (filename, + O_RDONLY); + if (-1 == fd) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + filename); + return GNUNET_SYSERR; + } + if (0 != + fstat (fd, + &sb)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + filename); + GNUNET_break (0 == close (fd)); + return GNUNET_OK; + } + + reply = MHD_create_response_from_fd (sb.st_size, + fd); + if (NULL == reply) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "open", + filename); + GNUNET_break (0 == close (fd)); + return GNUNET_OK; + } + + { + static struct MimeMap + { + const char *ext; + const char *mime; + } mm[] = { + { .ext = ".css", .mime = "text/css" }, + { .ext = ".js", .mime = "text/javascript" }, + { .ext = ".html", .mime = "text/html" }, + { .ext = ".htm", .mime = "text/html" }, + { .ext = ".txt", .mime = "text/plain" }, + { .ext = ".pdf", .mime = "application/pdf" }, + { .ext = ".jpg", .mime = "image/jpeg" }, + { .ext = ".jpeg", .mime = "image/jpeg" }, + { .ext = ".png", .mime = "image/png" }, + { .ext = ".apng", .mime = "image/apng" }, + { .ext = ".gif", .mime = "image/gif" }, + { .ext = ".svg", .mime = "image/svg+xml" }, + { .ext = ".tiff", .mime = "image/tiff" }, + { .ext = ".ico", .mime = "image/x-icon" }, + { .ext = ".bmp", .mime = "image/bmp" }, + { .ext = ".epub", .mime = "application/epub+zip" }, + { .ext = ".xml", .mime = "text/xml" }, + { .ext = NULL, .mime = NULL } + }; + const char *mime; + + mime = NULL; + for (unsigned int i = 0; NULL != mm[i].ext; i++) + if (0 == strcasecmp (mm[i].ext, + end)) + { + mime = mm[i].mime; + break; + } + if (NULL != mime) + GNUNET_break (MHD_NO != + MHD_add_response_header (reply, + MHD_HTTP_HEADER_CONTENT_TYPE, + mime)); + } + + GNUNET_array_grow (loaded, + loaded_length, + loaded_length + 1); + if (NULL != lang) + { + GNUNET_asprintf (&loaded[loaded_length - 1].name, + "%.*s%s", + (int) (lang - name) - 1, + name, + end); + loaded[loaded_length - 1].lang = GNUNET_strndup (lang, + end - lang); + } + else + { + loaded[loaded_length - 1].name = GNUNET_strdup (name); + } + loaded[loaded_length - 1].reply = reply; + return GNUNET_OK; +} + + +/** + * Preload static files. + */ +int +TMH_statics_init () +{ + char *dn; + int ret; + + { + char *path; + + path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR); + if (NULL == path) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + GNUNET_asprintf (&dn, + "%s/merchant/static/", + path); + GNUNET_free (path); + } + ret = GNUNET_DISK_directory_scan (dn, + &load_static_file, + NULL); + GNUNET_free (dn); + if (-1 == ret) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Nicely shut down. + */ +void __attribute__ ((destructor)) +get_statics_fini () +{ + for (unsigned int i = 0; i<loaded_length; i++) + { + GNUNET_free (loaded[i].name); + GNUNET_free (loaded[i].lang); + MHD_destroy_response (loaded[i].reply); + } + GNUNET_array_grow (loaded, + loaded_length, + 0); +} diff --git a/src/backend/taler-merchant-httpd_statics.h b/src/backend/taler-merchant-httpd_statics.h new file mode 100644 index 00000000..dac06a0d --- /dev/null +++ b/src/backend/taler-merchant-httpd_statics.h @@ -0,0 +1,49 @@ +/* + This file is part of TALER + Copyright (C) 2020 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 <http://www.gnu.org/licenses/> +*/ +/** + * @file merchant/backend/taler-merchant-httpd_statics.h + * @brief logic to preload and serve static files + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_STATICS_H +#define TALER_MERCHANT_HTTPD_STATICS_H + +#include <microhttpd.h> +#include "taler-merchant-httpd.h" + +/** + * Check for the existence of a static file and return the result if it + * matches. Otherwise returns a "not found" page. + * + * @param connection the connection we act upon + * @param template basename of the template to load + * @param taler_uri value for "Taler:" header to set, or NULL + * @param kvc key value pairs to fill in + * @return #MHD_YES on success (reply queued), #MHD_NO on error (close connection) + */ +MHD_RESULT +TMH_return_static (const struct TMH_RequestHandler *rh, + struct MHD_Connection *connection, + struct TMH_HandlerContext *hc); + +/** + * Preload static files. + */ +int +TMH_statics_init (void); + + +#endif diff --git a/src/backend/taler-merchant-httpd_templating.c b/src/backend/taler-merchant-httpd_templating.c index 50868adb..1b480a67 100644 --- a/src/backend/taler-merchant-httpd_templating.c +++ b/src/backend/taler-merchant-httpd_templating.c @@ -20,6 +20,7 @@ */ #include "platform.h" #include <gnunet/gnunet_util_lib.h> +#include <taler/taler_util.h> #include <taler/taler_mhd_lib.h> #include "taler-merchant-httpd_templating.h" #include "../mustach/mustach.h" @@ -196,10 +197,10 @@ lookup_template (struct MHD_Connection *connection, name)) continue; /* does not match by name */ if ( (NULL == best) || - (TALER_MHD_language_matches (lang, - loaded[i].lang) > - TALER_MHD_language_matches (lang, - best->lang) ) ) + (TALER_language_matches (lang, + loaded[i].lang) > + TALER_language_matches (lang, + best->lang) ) ) best = &loaded[i]; } if (NULL == best) @@ -469,7 +470,7 @@ TMH_templating_init () * Nicely shut down. */ void __attribute__ ((destructor)) -get_orders_fini () +templating_fini () { for (unsigned int i = 0; i<loaded_length; i++) { diff --git a/src/include/taler_merchant_service.h b/src/include/taler_merchant_service.h index b0be501f..16a01b99 100644 --- a/src/include/taler_merchant_service.h +++ b/src/include/taler_merchant_service.h @@ -179,7 +179,7 @@ struct TALER_MERCHANT_PayUriData * * @param pay_uri the URI to parse. * @param[out] parse_data data extracted from the URI. Must be free'd. - * @return GNUNET_SYSERR if @e pay_uri is malformed, GNUNET_OK otherwise. + * @return #GNUNET_SYSERR if @e pay_uri is malformed, #GNUNET_OK otherwise. */ int TALER_MERCHANT_parse_pay_uri (const char *pay_uri, diff --git a/src/lib/merchant_api_common.c b/src/lib/merchant_api_common.c index 123ad5bb..f375efd9 100644 --- a/src/lib/merchant_api_common.c +++ b/src/lib/merchant_api_common.c @@ -215,7 +215,7 @@ parse_taler_uri_scheme_action (const char *uri, * * @param pay_uri the URI to parse. * @param[out] parse_data data extracted from the URI. Must be free'd. - * @return GNUNET_SYSERR if @e pay_uri is malformed, GNUNET_OK otherwise. + * @return #GNUNET_SYSERR if @e pay_uri is malformed, #GNUNET_OK otherwise. */ int TALER_MERCHANT_parse_pay_uri (const char *pay_uri, @@ -232,6 +232,7 @@ TALER_MERCHANT_parse_pay_uri (const char *pay_uri, (0 != strcmp ("pay", action))) { + GNUNET_break_op (0); GNUNET_free (action); GNUNET_free (path); return GNUNET_SYSERR; @@ -249,6 +250,7 @@ TALER_MERCHANT_parse_pay_uri (const char *pay_uri, if (NULL == session_id) { + GNUNET_break_op (0); GNUNET_free (path); return GNUNET_SYSERR; } @@ -259,6 +261,7 @@ TALER_MERCHANT_parse_pay_uri (const char *pay_uri, '/'); if (NULL == order_id) { + GNUNET_break_op (0); GNUNET_free (path); return GNUNET_SYSERR; } @@ -289,7 +292,8 @@ TALER_MERCHANT_parse_pay_uri (const char *pay_uri, '='); if (NULL == ct_data) { - GNUNET_free (order_id); + GNUNET_break_op (0); + GNUNET_free (path); return GNUNET_SYSERR; } *ct_data = '\0'; @@ -303,7 +307,8 @@ TALER_MERCHANT_parse_pay_uri (const char *pay_uri, claim_token, sizeof (*claim_token)))) { - GNUNET_free (order_id); + GNUNET_break_op (0); + GNUNET_free (path); GNUNET_free (claim_token); return GNUNET_SYSERR; } |