gnunet-svn
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[taler-merchant] branch master updated: work on GET /tips/ HTML page


From: gnunet
Subject: [taler-merchant] branch master updated: work on GET /tips/ HTML page
Date: Mon, 03 Aug 2020 16:42:07 +0200

This is an automated email from the git hooks/post-receive script.

grothoff pushed a commit to branch master
in repository merchant.

The following commit(s) were added to refs/heads/master by this push:
     new ab07b05  work on GET /tips/ HTML page
ab07b05 is described below

commit ab07b05c353f7efa877804092f3529ffe7fb5665
Author: Christian Grothoff <christian@grothoff.org>
AuthorDate: Mon Aug 3 16:42:04 2020 +0200

    work on GET /tips/ HTML page
---
 contrib/Makefile.am                              |   5 +-
 contrib/depleted_tip.en.must                     |  51 +++
 contrib/offer_tip.en.must                        | 144 +++++++
 src/backend/taler-merchant-httpd_get-orders-ID.c |  39 +-
 src/backend/taler-merchant-httpd_get-tips-ID.c   | 145 ++++++-
 src/backend/taler-merchant-httpd_mhd.c           |  49 +++
 src/backend/taler-merchant-httpd_mhd.h           |   9 +
 src/backend/taler-merchant-httpd_templating.c    | 483 +++++++++++++++++++++++
 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 8edb184..c362ddd 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 0000000..370e9b9
--- /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 0000000..339f64f
--- /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 &copy; 2014&mdash;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 37dfd88..7ff0aed 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 52a0a56..487e88f 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 698c2ac..754c53b 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 cbf83ad..a3a9afb 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 0000000..50868ad
--- /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 0000000..e3c9d53
--- /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

-- 
To stop receiving notification emails like this one, please contact
gnunet@gnunet.org.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]