summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorJonathan Buchanan <jonathan.russ.buchanan@gmail.com>2020-08-20 00:49:30 -0400
committerJonathan Buchanan <jonathan.russ.buchanan@gmail.com>2020-08-20 01:01:05 -0400
commit9fce403bce4fe5f2be507a32ee7b4dfac296c26a (patch)
tree9c0c4509929e81f5b5eac6aa36e32d479c9643e3 /src
parent8d172853c1c7b1ca8d9dc31194c4e73e79499a80 (diff)
parentd6f28270094db1ffa4e5526ce556e7fc23e8e9b9 (diff)
downloadmerchant-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.am2
-rw-r--r--src/backend/taler-merchant-httpd.c9
-rw-r--r--src/backend/taler-merchant-httpd_get-orders-ID.c51
-rw-r--r--src/backend/taler-merchant-httpd_statics.c336
-rw-r--r--src/backend/taler-merchant-httpd_statics.h49
-rw-r--r--src/backend/taler-merchant-httpd_templating.c11
-rw-r--r--src/include/taler_merchant_service.h2
-rw-r--r--src/lib/merchant_api_common.c11
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;
}