diff options
-rw-r--r-- | contrib/Makefile.am | 5 | ||||
-rw-r--r-- | contrib/depleted_tip.en.must | 51 | ||||
-rw-r--r-- | contrib/offer_tip.en.must | 144 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_get-orders-ID.c | 39 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_get-tips-ID.c | 145 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_mhd.c | 49 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_mhd.h | 9 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_templating.c | 483 | ||||
-rw-r--r-- | src/backend/taler-merchant-httpd_templating.h | 70 |
9 files changed, 945 insertions, 50 deletions
diff --git a/contrib/Makefile.am b/contrib/Makefile.am index 8edb184b..c362ddd5 100644 --- a/contrib/Makefile.am +++ b/contrib/Makefile.am @@ -1,5 +1,8 @@ pkgdatadir = $(prefix)/share/taler/merchant/templates/ dist_pkgdata_DATA = \ + depleted_tip.en.must \ + offer_refund.en.must \ + offer_tip.en.must \ request_payment.en.must \ - offer_refund.en.must + show_order_details.en.must diff --git a/contrib/depleted_tip.en.must b/contrib/depleted_tip.en.must new file mode 100644 index 00000000..370e9b90 --- /dev/null +++ b/contrib/depleted_tip.en.must @@ -0,0 +1,51 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014-2020 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 + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +--> + +<html data-taler-nojs="true"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <noscript> + <meta http-equiv="refresh" content="1"> + </noscript> + <title>Status of your tip</title> + <!-- FIXME: inline this? How to best serve this without using 3rd party? --> + <link rel="stylesheet" + href="https://unpkg.com/purecss@2.0.3/build/pure-min.css" + integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ" + crossorigin="anonymous"> + <style> +.content { + overflow-x: auto; + padding-left: 15%; + padding-right: 15%; +} +#main a:link, #main a:visited, #main a:hover, #main a:active { + color: black; +} + </style> +</head> + +<body> +<h1>Tip already picked up</h1> + +<div> +You have already picked up your tip. +</div> + +</body> +</html> diff --git a/contrib/offer_tip.en.must b/contrib/offer_tip.en.must new file mode 100644 index 00000000..339f64ff --- /dev/null +++ b/contrib/offer_tip.en.must @@ -0,0 +1,144 @@ +<!DOCTYPE html> +<!-- + This file is part of GNU TALER. + Copyright (C) 2014-2020 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 + Foundation; either version 2.1, 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 Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License along with + TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> +--> + +<html data-taler-nojs="true"> +<head> + <meta charset="UTF-8"> + <meta name="viewport" content="width=device-width, initial-scale=1.0"> + <noscript> + <meta http-equiv="refresh" content="1"> + </noscript> + <title>Tip available</title> + <!-- FIXME-6460: allow taler-merchant-httpd to serve this, so we do not use 3rd party? --> + <link rel="stylesheet" + href="https://unpkg.com/purecss@2.0.3/build/pure-min.css" + integrity="sha384-cg6SkqEOCV1NbJoCu11+bm0NvBRc8IYLRGXkmNrqUBfTjmMYwNKPWBTIKyw9mHNJ" + crossorigin="anonymous"> + <style> +.taler::before { + content: "❬"; + color: #aa3939; +} +.taler::after { + content: "❭"; + color: #aa3939; +} +.talerbar { + text-align: center; +} +.tt { + font-family: 'Lucida Console', Monaco, monospace; +} +.content { + overflow-x: auto; + padding-left: 15%; + padding-right: 15%; +} +.qr { + margin: auto; + text-align: center; +} +.qrtext { + width: max-content; + margin: auto; + transition: font-size 0.2s; + font-family: 'Lucida Console', Monaco, monospace; + font-size: 0.5em; +} +.qrtext:hover { + font-size: 1em; +} +.talerbar { + margin: 0; + bottom: 0; + background-color: #033; + color: white; + width: 100%; + padding: 1em; + overflow: auto; +} + +body { + overflow-y: scroll; +} +@media (min-width: 500px) { + .content { + padding-bottom: 2em; + margin-right: 1em; + overflow-y: auto; + } +} +#main a:link, #main a:visited, #main a:hover, #main a:active { + color: black; +} + </style> +</head> + +<body> +<script> + let checkUrl = FIXME-#6457_dold_tip_uri_to_URL("{{taler_tip_uri}}"); + let delayMs = 500; + function check() { + let req = new XMLHttpRequest(); + req.onreadystatechange = function () { + if (req.readyState === XMLHttpRequest.DONE) { + if (req.status === 410) { + document.location.reload(true); + } + setTimeout(check, delayMs); + } + }; + req.onerror = function () { + setTimeout(check, delayMs); + } + req.open("GET", checkUrl); + req.send(); + } + + setTimeout(check, delayMs); +</script> + + +<h1><span class="taler">Taler</span> tip available</h1> + +<div class="taler-installed-hide"> + <p> + Please select your Taler wallet to pick up the tip. + </p> +</div> + +<div> + <p> + Alternatively, you can scan this QR code to pick up the tip with your mobile wallet: + </p> + <div class="qr"> + {{{taler_tip_qrcode_svg}}} + </div> + <p> + Finally, you could click <a href="{{taler_tip_uri}}">this link</a> to + try to open your system's Taler wallet if it exists. + </p> +</div> +<hr /> +</section> + +<div class="talerbar"> + <p>You can learn more about GNU Taler on our <a href="https://taler.net/">website</a>.<br> + Copyright © 2014—2020 Taler Systems SA</p> +</div> +</body> +</html> diff --git a/src/backend/taler-merchant-httpd_get-orders-ID.c b/src/backend/taler-merchant-httpd_get-orders-ID.c index 37dfd884..7ff0aedc 100644 --- a/src/backend/taler-merchant-httpd_get-orders-ID.c +++ b/src/backend/taler-merchant-httpd_get-orders-ID.c @@ -855,44 +855,7 @@ TMH_get_orders_ID (const struct TMH_RequestHandler *rh, } } - { /* check for 'Accept' header */ - const char *accept; - - accept = MHD_lookup_connection_value (connection, - MHD_HEADER_KIND, - MHD_HTTP_HEADER_ACCEPT); - if (NULL != accept) - { - char *a = GNUNET_strdup (accept); - char *saveptr; - - for (char *t = strtok_r (a, ",", &saveptr); - NULL != t; - t = strtok_r (NULL, ",", &saveptr)) - { - char *end; - - /* skip leading whitespace */ - while (isspace ((unsigned char) t[0])) - t++; - /* trim of ';q=' parameter and everything after space */ - end = strchr (t, ';'); - if (NULL != end) - *end = '\0'; - end = strchr (t, ' '); - if (NULL != end) - *end = '\0'; - if (0 == strcasecmp ("text/html", - t)) - { - god->generate_html = true; - break; - } - } - GNUNET_free (a); - } - } /* end check for 'Accept' header */ - + god->generate_html = TMH_MHD_test_html_desired (connection); { const char *long_poll_timeout_s; diff --git a/src/backend/taler-merchant-httpd_get-tips-ID.c b/src/backend/taler-merchant-httpd_get-tips-ID.c index 52a0a561..487e88f4 100644 --- a/src/backend/taler-merchant-httpd_get-tips-ID.c +++ b/src/backend/taler-merchant-httpd_get-tips-ID.c @@ -24,6 +24,79 @@ #include <taler/taler_signatures.h> #include <taler/taler_json_lib.h> #include "taler-merchant-httpd_get-tips-ID.h" +#include "taler-merchant-httpd_mhd.h" +#include "taler-merchant-httpd_qr.h" +#include "taler-merchant-httpd_templating.h" + + +/** + * Create a taler://tip/ URI for the given @a con and @a tip_id + * and @a instance_id. + * + * @param con HTTP connection + * @param tip_id the tip id + * @param instance_id instance, may be "default" + * @return corresponding taler://tip/ URI, or NULL on missing "host" + */ +static char * +make_taler_tip_uri (struct MHD_Connection *con, + const struct GNUNET_HashCode *tip_id, + const char *instance_id) +{ + const char *host; + const char *forwarded_host; + const char *uri_path; + struct GNUNET_Buffer buf = { 0 }; + + host = MHD_lookup_connection_value (con, + MHD_HEADER_KIND, + "Host"); + forwarded_host = MHD_lookup_connection_value (con, + MHD_HEADER_KIND, + "X-Forwarded-Host"); + + uri_path = MHD_lookup_connection_value (con, + MHD_HEADER_KIND, + "X-Forwarded-Prefix"); + if (NULL != forwarded_host) + host = forwarded_host; + + if (NULL == host) + { + GNUNET_break (0); + return NULL; + } + + GNUNET_assert (NULL != instance_id); + GNUNET_assert (NULL != tip_id); + + GNUNET_buffer_write_str (&buf, + "taler"); + if (GNUNET_NO == TALER_mhd_is_https (con)) + GNUNET_buffer_write_str (&buf, + "+http"); + GNUNET_buffer_write_str (&buf, + "://tip/"); + GNUNET_buffer_write_str (&buf, + host); + if (NULL != uri_path) + GNUNET_buffer_write_path (&buf, + uri_path); + if (0 != strcmp ("default", + instance_id)) + { + GNUNET_buffer_write_path (&buf, + "instances"); + GNUNET_buffer_write_path (&buf, + instance_id); + } + GNUNET_buffer_write_data_encoded (&buf, + tip_id, + sizeof (*tip_id)); + GNUNET_buffer_write_str (&buf, + "/"); + return GNUNET_buffer_reap_str (&buf); +} /** @@ -93,9 +166,9 @@ TMH_get_tips_ID (const struct TMH_RequestHandler *rh, /* Build response */ { - MHD_RESULT ret; struct TALER_Amount remaining; struct GNUNET_TIME_Absolute expiration_round = expiration; + MHD_RESULT ret; GNUNET_break (0 <= TALER_amount_subtract (&remaining, @@ -103,17 +176,67 @@ TMH_get_tips_ID (const struct TMH_RequestHandler *rh, &total_picked_up)); GNUNET_TIME_round_abs (&expiration_round); + if (TMH_MHD_test_html_desired (connection)) + { + char *qr; + char *uri; + + uri = make_taler_tip_uri (connection, + &tip_id, + hc->instance->settings.id); + qr = TMH_create_qrcode (uri); + if (NULL == qr) + { + GNUNET_break (0); + GNUNET_free (uri); + ret = TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_ALLOCATION_FAILURE, + "during QR code generation"); + } + else + { + struct KVC kvc[] = { + { "remaining_tip", + TALER_amount2s (&remaining) }, + { "taler_tip_uri", + uri }, + { "taler_tip_qrcode_svg", + qr }, + { NULL, NULL } + }; - ret = TALER_MHD_reply_json_pack (connection, - MHD_HTTP_OK, - "{s:s, s:o, s:o}", - "exchange_url", - exchange_url, - "tip_amount", - TALER_JSON_from_amount (&remaining), - "expiration", - GNUNET_JSON_from_time_abs ( - expiration_round)); + ret = TMH_return_from_template (connection, + ( (0 == remaining.value) && + (0 == remaining.fraction) ) + ? MHD_HTTP_GONE + : MHD_HTTP_OK, + ( (0 == remaining.value) && + (0 == remaining.fraction) ) + ? "depleted_tip" + : "offer_tip", + uri, + kvc); + } + GNUNET_free (uri); + GNUNET_free (qr); + } + else + { + ret = TALER_MHD_reply_json_pack (connection, + ( (0 == remaining.value) && + (0 == remaining.fraction) ) + ? MHD_HTTP_GONE + : MHD_HTTP_OK, + "{s:s, s:o, s:o}", + "exchange_url", + exchange_url, + "tip_amount", + TALER_JSON_from_amount (&remaining), + "expiration", + GNUNET_JSON_from_time_abs ( + expiration_round)); + } GNUNET_free (exchange_url); return ret; } diff --git a/src/backend/taler-merchant-httpd_mhd.c b/src/backend/taler-merchant-httpd_mhd.c index 698c2ac5..754c53b1 100644 --- a/src/backend/taler-merchant-httpd_mhd.c +++ b/src/backend/taler-merchant-httpd_mhd.c @@ -71,4 +71,53 @@ TMH_MHD_handler_agpl_redirect (const struct TMH_RequestHandler *rh, } +/** + * Test if the client requested HTML output. + * + * @param connection client to test + * @return true if HTML was requested + */ +bool +TMH_MHD_test_html_desired (struct MHD_Connection *connection) +{ + bool ret; + const char *accept; + + accept = MHD_lookup_connection_value (connection, + MHD_HEADER_KIND, + MHD_HTTP_HEADER_ACCEPT); + if (NULL != accept) + { + char *a = GNUNET_strdup (accept); + char *saveptr; + + for (char *t = strtok_r (a, ",", &saveptr); + NULL != t; + t = strtok_r (NULL, ",", &saveptr)) + { + char *end; + + /* skip leading whitespace */ + while (isspace ((unsigned char) t[0])) + t++; + /* trim of ';q=' parameter and everything after space */ + end = strchr (t, ';'); + if (NULL != end) + *end = '\0'; + end = strchr (t, ' '); + if (NULL != end) + *end = '\0'; + if (0 == strcasecmp ("text/html", + t)) + { + ret = true; + break; + } + } + GNUNET_free (a); + } + return ret; +} + + /* end of taler-exchange-httpd_mhd.c */ diff --git a/src/backend/taler-merchant-httpd_mhd.h b/src/backend/taler-merchant-httpd_mhd.h index cbf83add..a3a9afbc 100644 --- a/src/backend/taler-merchant-httpd_mhd.h +++ b/src/backend/taler-merchant-httpd_mhd.h @@ -102,4 +102,13 @@ TMH_MHD_handler_send_json_pack_error (struct TMH_RequestHandler *rh, struct TMH_MerchantInstance *mi); +/** + * Test if the client requested HTML output. + * + * @param connection client to test + * @return true if HTML was requested + */ +bool +TMH_MHD_test_html_desired (struct MHD_Connection *connection); + #endif diff --git a/src/backend/taler-merchant-httpd_templating.c b/src/backend/taler-merchant-httpd_templating.c new file mode 100644 index 00000000..50868adb --- /dev/null +++ b/src/backend/taler-merchant-httpd_templating.c @@ -0,0 +1,483 @@ +/* + 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_templating.c + * @brief logic to load and complete HTML templates + * @author Christian Grothoff + */ +#include "platform.h" +#include <gnunet/gnunet_util_lib.h> +#include <taler/taler_mhd_lib.h> +#include "taler-merchant-httpd_templating.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; + + /** + * 0-terminated (!) file data to return for @e name and @e lang. + */ + char *value; + +}; + + +/** + * Array of templates loaded into RAM. + */ +static struct TVE *loaded; + +/** + * Length of the #loaded array. + */ +static unsigned int loaded_length; + + +/** + * Function called by Mustach to enter the section @a name. + * As we do not support sections, we always return 0. + * + * @param cls a `struct KVC[]` array + * @param name section to enter + * @return 0 (do not enter) + */ +static int +m_enter (void *cls, const char *name) +{ + (void) cls; + (void) name; + return 0; +} + + +/** + * Function called by mustach to activate the next item in the + * section. Does nothing, as we do not support sections. + * + * @param cls a `struct KVC[]` array + * @return 0 (no next item to activate) + */ +static int +m_next (void *cls) +{ + (void) cls; + return 0; +} + + +/** + * Function called by Mustach to leave the current section. + * As we do not support sections, we should never be called. + * + * @param cls a `struct KVC[]` array + * @return 0 (not documented by mustach) + */ +static int +m_leave (void *cls) +{ + GNUNET_assert (0); + return 0; +} + + +/** + * Return the value of @a name in @a sbuf. + * + * @param cls a `struct KVC[]` array + * @param name the value to lookup + * @param[out] sbuf where to return the data + * @return mustach-defined status code + */ +static int +m_get (void *cls, + const char *name, + struct mustach_sbuf *sbuf) +{ + const struct KVC *kvc = cls; + + for (unsigned int i = 0; NULL != kvc[i].name; i++) + { + if (0 == strcmp (name, + kvc[i].name)) + { + sbuf->value = kvc[i].value; + sbuf->releasecb = NULL; + sbuf->closure = NULL; + return MUSTACH_OK; + } + } + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Template requires value for unexpected name `%s'\n", + name); + return MUSTACH_ERROR_ITEM_NOT_FOUND; +} + + +/** + * Mustach callback at the end. Cleans up the @a cls. + * + * @param cls a `struct KVC[]` array + * @param status status of mustach (ignored) + */ +static void +m_stop (void *cls, + int status) +{ + (void) cls; + (void) status; +} + + +/** + * Our 'universal' callbacks for mustach. + */ +static struct mustach_itf itf = { + .enter = &m_enter, + .next = &m_next, + .leave = &m_leave, + .get = &m_get, + .stop = &m_stop +}; + + +/** + * 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 char * +lookup_template (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 == best) || + (TALER_MHD_language_matches (lang, + loaded[i].lang) > + TALER_MHD_language_matches (lang, + best->lang) ) ) + best = &loaded[i]; + } + if (NULL == best) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "No templates found in `%s'\n", + name); + return NULL; + } + return best->value; +} + + +/** + * Load a @a template and substitute using @a kvc, returning + * the result to the @a connection with the given + * @a http_status code. + * + * @param connection the connection we act upon + * @param http_status desired HTTP status code + * @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 #GNUNET_OK on success (reply queued), #GNUNET_NO if an error was queued, + * #GNUNET_SYSERR on failure (to queue an error) + */ +enum GNUNET_GenericReturnValue +TMH_return_from_template (struct MHD_Connection *connection, + unsigned int http_status, + const char *template, + const char *taler_uri, + const struct KVC *kvc) +{ + struct MHD_Response *reply; + char *body; + size_t body_size; + + { + const char *tmpl; + int eno; + + tmpl = lookup_template (connection, + template); + if (NULL == tmpl) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Failed to load template `%s'\n", + template); + if (MHD_YES != + TALER_MHD_reply_with_error (connection, + MHD_HTTP_NOT_ACCEPTABLE, + TALER_EC_MERCHANT_FAILED_TO_LOAD_TEMPLATE, + template)) + return GNUNET_SYSERR; + return GNUNET_NO; + } + if (0 != + (eno = mustach (tmpl, + &itf, + (void *) kvc, + &body, + &body_size))) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "mustach failed on template `%s' with error %d\n", + template, + eno); + if (MHD_YES != + TALER_MHD_reply_with_error (connection, + MHD_HTTP_INTERNAL_SERVER_ERROR, + TALER_EC_MERCHANT_FAILED_TO_EXPAND_TEMPLATE, + template)) + return GNUNET_SYSERR; + return GNUNET_NO; + } + } + + /* try to compress reply if client allows it */ + { + bool compressed = false; + + if (MHD_YES == + TALER_MHD_can_compress (connection)) + { + compressed = TALER_MHD_body_compress ((void **) &body, + &body_size); + } + reply = MHD_create_response_from_buffer (body_size, + body, + MHD_RESPMEM_MUST_FREE); + if (NULL == reply) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (compressed) + { + if (MHD_NO == + MHD_add_response_header (reply, + MHD_HTTP_HEADER_CONTENT_ENCODING, + "deflate")) + { + GNUNET_break (0); + MHD_destroy_response (reply); + return GNUNET_SYSERR; + } + } + } + + /* Add standard headers */ + if (NULL != taler_uri) + GNUNET_break (MHD_NO != + MHD_add_response_header (reply, + "Taler", + taler_uri)); + GNUNET_break (MHD_NO != + MHD_add_response_header (reply, + MHD_HTTP_HEADER_CONTENT_TYPE, + "text/html")); + + /* Actually return reply */ + { + MHD_RESULT ret; + + ret = MHD_queue_response (connection, + http_status, + reply); + MHD_destroy_response (reply); + if (MHD_NO == ret) + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Function called with a template'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_template (void *cls, + const char *filename) +{ + char *lang; + char *end; + int fd; + struct stat sb; + char *map; + const char *name; + + 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 .$LANG */ + lang++; + end = strchr (lang, + '.'); + if ( (NULL == end) || + (0 != strcmp (end, + ".must")) ) + return GNUNET_OK; /* name must end with '.must' */ + + /* 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; + } + map = GNUNET_malloc_large (sb.st_size + 1); + if (NULL == map) + { + GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, + "malloc"); + GNUNET_break (0 == close (fd)); + return GNUNET_SYSERR; + } + if (sb.st_size != + read (fd, + map, + sb.st_size)) + { + GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, + "read", + filename); + GNUNET_break (0 == close (fd)); + return GNUNET_OK; + } + GNUNET_break (0 == close (fd)); + GNUNET_array_grow (loaded, + loaded_length, + loaded_length + 1); + loaded[loaded_length - 1].name = GNUNET_strndup (name, + (lang - 1) - name); + loaded[loaded_length - 1].lang = GNUNET_strndup (lang, + end - lang); + loaded[loaded_length - 1].value = map; + return GNUNET_OK; +} + + +/** + * Preload templates. + */ +int +TMH_templating_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/templates/", + path); + GNUNET_free (path); + } + ret = GNUNET_DISK_directory_scan (dn, + &load_template, + NULL); + GNUNET_free (dn); + if (-1 == ret) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + return GNUNET_OK; +} + + +/** + * Nicely shut down. + */ +void __attribute__ ((destructor)) +get_orders_fini () +{ + for (unsigned int i = 0; i<loaded_length; i++) + { + GNUNET_free (loaded[i].name); + GNUNET_free (loaded[i].lang); + GNUNET_free (loaded[i].value); + } + GNUNET_array_grow (loaded, + loaded_length, + 0); +} diff --git a/src/backend/taler-merchant-httpd_templating.h b/src/backend/taler-merchant-httpd_templating.h new file mode 100644 index 00000000..e3c9d53e --- /dev/null +++ b/src/backend/taler-merchant-httpd_templating.h @@ -0,0 +1,70 @@ +/* + 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_templating.h + * @brief logic to load and complete HTML templates + * @author Christian Grothoff + */ +#ifndef TALER_MERCHANT_HTTPD_TEMPLATING_H +#define TALER_MERCHANT_HTTPD_TEMPLATING_H + +#include <microhttpd.h> + +/** + * Entry in a key-value array we use as the mustach closure. + */ +struct KVC +{ + /** + * A name, used as the key. NULL for the last entry. + */ + const char *name; + + /** + * 0-terminated string value to return for @e name. + */ + const char *value; +}; + + +/** + * Load a @a template and substitute using @a kvc, returning + * the result to the @a connection with the given + * @a http_status code. + * + * @param connection the connection we act upon + * @param http_status code to use on success + * @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 #GNUNET_OK on success (reply queued), #GNUNET_NO if an error was queued, + * #GNUNET_SYSERR on failure (to queue an error) + */ +enum GNUNET_GenericReturnValue +TMH_return_from_template (struct MHD_Connection *connection, + unsigned int http_status, + const char *template, + const char *taler_uri, + const struct KVC *kvc); + +/** + * Preload templates. + */ +int +TMH_templating_init (void); + + +#endif |