aboutsummaryrefslogtreecommitdiff
path: root/src/mhd
diff options
context:
space:
mode:
authorChristian Grothoff <christian@grothoff.org>2024-06-03 13:37:56 +0200
committerChristian Grothoff <christian@grothoff.org>2024-07-29 12:18:42 +0200
commitb65481ad8fae5fe7fad9cb0253449fdc80bf7ff8 (patch)
tree52622c63602cd70bca663e9aee745ff0898f2cae /src/mhd
parent4968c2ed31d8d76af3f2864a06c453982f3898ec (diff)
add spa logic
Diffstat (limited to 'src/mhd')
-rw-r--r--src/mhd/Makefile.am3
-rw-r--r--src/mhd/mhd_spa.c367
2 files changed, 369 insertions, 1 deletions
diff --git a/src/mhd/Makefile.am b/src/mhd/Makefile.am
index 703e34ca4..563ebf398 100644
--- a/src/mhd/Makefile.am
+++ b/src/mhd/Makefile.am
@@ -14,7 +14,8 @@ libtalermhd_la_SOURCES = \
mhd_legal.c \
mhd_parsing.c \
mhd_responses.c \
- mhd_run.c
+ mhd_run.c \
+ mhd_spa.c
libtalermhd_la_LDFLAGS = \
-version-info 3:1:3 \
-no-undefined
diff --git a/src/mhd/mhd_spa.c b/src/mhd/mhd_spa.c
new file mode 100644
index 000000000..c4aff281d
--- /dev/null
+++ b/src/mhd/mhd_spa.c
@@ -0,0 +1,367 @@
+/*
+ This file is part of TALER
+ Copyright (C) 2020, 2023, 2024 Taler Systems SA
+
+ TALER is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+*/
+/**
+ * @file mhd_spa.c
+ * @brief logic to load single page apps
+ * @author Christian Grothoff
+ */
+#include "platform.h"
+#include <gnunet/gnunet_util_lib.h>
+#include "taler_util.h"
+#include "taler_mhd_lib.h"
+#include <gnunet/gnunet_mhd_compat.h>
+
+
+/**
+ * Resource from the WebUi.
+ */
+struct WebuiFile
+{
+ /**
+ * Kept in a DLL.
+ */
+ struct WebuiFile *next;
+
+ /**
+ * Kept in a DLL.
+ */
+ struct WebuiFile *prev;
+
+ /**
+ * Path this resource matches.
+ */
+ char *path;
+
+ /**
+ * SPA resource, compressed.
+ */
+ struct MHD_Response *zresponse;
+
+ /**
+ * SPA resource, vanilla.
+ */
+ struct MHD_Response *response;
+
+};
+
+
+/**
+ * Resource from the WebUi.
+ */
+struct TALER_MHD_Spa
+{
+ /**
+ * Resources of the WebUI, kept in a DLL.
+ */
+ struct WebuiFile *webui_head;
+
+ /**
+ * Resources of the WebUI, kept in a DLL.
+ */
+ struct WebuiFile *webui_tail;
+};
+
+
+MHD_RESULT
+TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa,
+ struct MHD_Connection *connection,
+ const char *path)
+{
+ struct WebuiFile *w = NULL;
+
+ if ( (NULL == path) ||
+ (0 == strcmp (path,
+ "")) )
+ path = "index.html";
+ for (struct WebuiFile *pos = spa->webui_head;
+ NULL != pos;
+ pos = pos->next)
+ if (0 == strcmp (path,
+ pos->path))
+ {
+ w = pos;
+ break;
+ }
+ if (NULL == w)
+ return TALER_MHD_reply_with_error (connection,
+ MHD_HTTP_NOT_FOUND,
+ TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
+ path);
+ if ( (MHD_YES ==
+ TALER_MHD_can_compress (connection)) &&
+ (NULL != w->zresponse) )
+ return MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ w->zresponse);
+ return MHD_queue_response (connection,
+ MHD_HTTP_OK,
+ w->response);
+}
+
+
+/**
+ * Function called on each file to load for the WebUI.
+ *
+ * @param cls the `struct TALER_MHD_Spa *` to build
+ * @param dn name of the file to load
+ */
+static enum GNUNET_GenericReturnValue
+build_webui (void *cls,
+ const char *dn)
+{
+ static const struct
+ {
+ const char *ext;
+ const char *mime;
+ } mime_map[] = {
+ {
+ .ext = "css",
+ .mime = "text/css"
+ },
+ {
+ .ext = "html",
+ .mime = "text/html"
+ },
+ {
+ .ext = "js",
+ .mime = "text/javascript"
+ },
+ {
+ .ext = "jpg",
+ .mime = "image/jpeg"
+ },
+ {
+ .ext = "jpeg",
+ .mime = "image/jpeg"
+ },
+ {
+ .ext = "png",
+ .mime = "image/png"
+ },
+ {
+ .ext = "svg",
+ .mime = "image/svg+xml"
+ },
+ {
+ .ext = NULL,
+ .mime = NULL
+ },
+ };
+ struct TALER_MHD_Spa *spa = cls;
+ int fd;
+ struct stat sb;
+ struct MHD_Response *zresponse = NULL;
+ struct MHD_Response *response;
+ const char *ext;
+ const char *mime;
+
+ fd = open (dn,
+ O_RDONLY);
+ if (-1 == fd)
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "open",
+ dn);
+ return GNUNET_SYSERR;
+ }
+ if (0 !=
+ fstat (fd,
+ &sb))
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "open",
+ dn);
+ GNUNET_break (0 == close (fd));
+ return GNUNET_SYSERR;
+ }
+
+ mime = NULL;
+ ext = strrchr (dn, '.');
+ if (NULL == ext)
+ {
+ GNUNET_break (0 == close (fd));
+ return GNUNET_OK;
+ }
+ ext++;
+ for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
+ if (0 == strcasecmp (ext,
+ mime_map[i].ext))
+ {
+ mime = mime_map[i].mime;
+ break;
+ }
+
+ {
+ void *in;
+ ssize_t r;
+ size_t csize;
+
+ in = GNUNET_malloc_large (sb.st_size);
+ if (NULL == in)
+ {
+ GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
+ "malloc");
+ GNUNET_break (0 == close (fd));
+ return GNUNET_SYSERR;
+ }
+ r = read (fd,
+ in,
+ sb.st_size);
+ if ( (-1 == r) ||
+ (sb.st_size != (size_t) r) )
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "read",
+ dn);
+ GNUNET_free (in);
+ GNUNET_break (0 == close (fd));
+ return GNUNET_SYSERR;
+ }
+ csize = (size_t) r;
+ if (MHD_YES ==
+ TALER_MHD_body_compress (&in,
+ &csize))
+ {
+ zresponse = MHD_create_response_from_buffer (csize,
+ in,
+ MHD_RESPMEM_MUST_FREE);
+ if (NULL != zresponse)
+ {
+ if (MHD_NO ==
+ MHD_add_response_header (zresponse,
+ MHD_HTTP_HEADER_CONTENT_ENCODING,
+ "deflate"))
+ {
+ GNUNET_break (0);
+ MHD_destroy_response (zresponse);
+ zresponse = NULL;
+ }
+ if (NULL != mime)
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (zresponse,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ mime));
+ }
+ }
+ else
+ {
+ GNUNET_free (in);
+ }
+ }
+
+ response = MHD_create_response_from_fd (sb.st_size,
+ fd);
+ if (NULL == response)
+ {
+ GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
+ "open",
+ dn);
+ GNUNET_break (0 == close (fd));
+ if (NULL != zresponse)
+ {
+ MHD_destroy_response (zresponse);
+ zresponse = NULL;
+ }
+ return GNUNET_SYSERR;
+ }
+ if (NULL != mime)
+ GNUNET_break (MHD_YES ==
+ MHD_add_response_header (response,
+ MHD_HTTP_HEADER_CONTENT_TYPE,
+ mime));
+
+ {
+ struct WebuiFile *w;
+ const char *fn;
+
+ fn = strrchr (dn, '/');
+ GNUNET_assert (NULL != fn);
+ w = GNUNET_new (struct WebuiFile);
+ w->path = GNUNET_strdup (fn + 1);
+ w->response = response;
+ w->zresponse = zresponse;
+ GNUNET_CONTAINER_DLL_insert (spa->webui_head,
+ spa->webui_tail,
+ w);
+ }
+ return GNUNET_OK;
+}
+
+
+struct TALER_MHD_Spa *
+TALER_MHD_spa_load (const char *dir)
+{
+ struct TALER_MHD_Spa *spa;
+ char *dn;
+
+ {
+ char *path;
+
+ path = GNUNET_OS_installation_get_path (GNUNET_OS_IPK_DATADIR);
+ if (NULL == path)
+ {
+ GNUNET_break (0);
+ return NULL;
+ }
+ GNUNET_asprintf (&dn,
+ "%s%s",
+ path,
+ dir);
+ GNUNET_free (path);
+ }
+ spa = GNUNET_new (struct TALER_MHD_Spa);
+ if (-1 ==
+ GNUNET_DISK_directory_scan (dn,
+ &build_webui,
+ spa))
+ {
+ GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
+ "Failed to load WebUI from `%s'\n",
+ dn);
+ GNUNET_free (dn);
+ TALER_MHD_spa_free (spa);
+ return NULL;
+ }
+ GNUNET_free (dn);
+ return spa;
+}
+
+
+void
+TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
+{
+ struct WebuiFile *w;
+
+ while (NULL != (w = spa->webui_head))
+ {
+ GNUNET_CONTAINER_DLL_remove (spa->webui_head,
+ spa->webui_tail,
+ w);
+ if (NULL != w->response)
+ {
+ MHD_destroy_response (w->response);
+ w->response = NULL;
+ }
+ if (NULL != w->zresponse)
+ {
+ MHD_destroy_response (w->zresponse);
+ w->zresponse = NULL;
+ }
+ GNUNET_free (w->path);
+ GNUNET_free (w);
+ }
+ GNUNET_free (spa);
+}