LCOV - code coverage report
Current view: top level - mhd - mhd_spa.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 78 120 65.0 %
Date: 2025-08-30 09:28:00 Functions: 3 4 75.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2020, 2023, 2024 Taler Systems SA
       4             : 
       5             :   TALER is free software; you can redistribute it and/or modify it under the
       6             :   terms of the GNU General Public License as published by the Free Software
       7             :   Foundation; either version 3, or (at your option) any later version.
       8             : 
       9             :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10             :   WARRANTY; without even the implied warranty of EXCHANGEABILITY or FITNESS FOR
      11             :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12             : 
      13             :   You should have received a copy of the GNU General Public License along with
      14             :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15             : */
      16             : /**
      17             :  * @file mhd_spa.c
      18             :  * @brief logic to load single page apps
      19             :  * @author Christian Grothoff
      20             :  */
      21             : #include "taler/platform.h"
      22             : #include <gnunet/gnunet_util_lib.h>
      23             : #include "taler/taler_util.h"
      24             : #include "taler/taler_mhd_lib.h"
      25             : #include <gnunet/gnunet_mhd_compat.h>
      26             : 
      27             : 
      28             : /**
      29             :  * Resource from the WebUi.
      30             :  */
      31             : struct WebuiFile
      32             : {
      33             :   /**
      34             :    * Kept in a DLL.
      35             :    */
      36             :   struct WebuiFile *next;
      37             : 
      38             :   /**
      39             :    * Kept in a DLL.
      40             :    */
      41             :   struct WebuiFile *prev;
      42             : 
      43             :   /**
      44             :    * Path this resource matches.
      45             :    */
      46             :   char *path;
      47             : 
      48             :   /**
      49             :    * SPA resource, deflate compressed.
      50             :    */
      51             :   struct MHD_Response *responses[TALER_MHD_CT_MAX];
      52             : 
      53             : };
      54             : 
      55             : 
      56             : /**
      57             :  * Resource from the WebUi.
      58             :  */
      59             : struct TALER_MHD_Spa
      60             : {
      61             :   /**
      62             :    * Resources of the WebUI, kept in a DLL.
      63             :    */
      64             :   struct WebuiFile *webui_head;
      65             : 
      66             :   /**
      67             :    * Resources of the WebUI, kept in a DLL.
      68             :    */
      69             :   struct WebuiFile *webui_tail;
      70             : };
      71             : 
      72             : 
      73             : MHD_RESULT
      74           0 : TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa,
      75             :                        struct MHD_Connection *connection,
      76             :                        const char *path)
      77             : {
      78           0 :   struct WebuiFile *w = NULL;
      79             :   enum TALER_MHD_CompressionType comp;
      80             : 
      81           0 :   if ( (NULL == path) ||
      82           0 :        (0 == strcmp (path,
      83             :                      "")) )
      84           0 :     path = "index.html";
      85           0 :   for (struct WebuiFile *pos = spa->webui_head;
      86           0 :        NULL != pos;
      87           0 :        pos = pos->next)
      88           0 :     if (0 == strcmp (path,
      89           0 :                      pos->path))
      90             :     {
      91           0 :       w = pos;
      92           0 :       break;
      93             :     }
      94           0 :   if ( (NULL == w) ||
      95           0 :        (NULL == w->responses[TALER_MHD_CT_NONE]) )
      96           0 :     return TALER_MHD_reply_with_error (connection,
      97             :                                        MHD_HTTP_NOT_FOUND,
      98             :                                        TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
      99             :                                        path);
     100           0 :   comp = TALER_MHD_can_compress (connection,
     101             :                                  TALER_MHD_CT_MAX - 1);
     102           0 :   GNUNET_assert (comp < TALER_MHD_CT_MAX);
     103             :   GNUNET_assert (comp >= 0);
     104           0 :   if (NULL != w->responses[comp])
     105           0 :     return MHD_queue_response (connection,
     106             :                                MHD_HTTP_OK,
     107             :                                w->responses[comp]);
     108           0 :   return MHD_queue_response (connection,
     109             :                              MHD_HTTP_OK,
     110             :                              w->responses[TALER_MHD_CT_NONE]);
     111             : }
     112             : 
     113             : 
     114             : /**
     115             :  * Function called on each file to load for the WebUI.
     116             :  *
     117             :  * @param cls the `struct TALER_MHD_Spa *` to build
     118             :  * @param dn name of the file to load
     119             :  */
     120             : static enum GNUNET_GenericReturnValue
     121         888 : build_webui (void *cls,
     122             :              const char *dn)
     123             : {
     124             :   static const struct
     125             :   {
     126             :     const char *ext;
     127             :     const char *mime;
     128             :   } mime_map[] = {
     129             :     {
     130             :       .ext = "css",
     131             :       .mime = "text/css"
     132             :     },
     133             :     {
     134             :       .ext = "html",
     135             :       .mime = "text/html"
     136             :     },
     137             :     {
     138             :       .ext = "js",
     139             :       .mime = "text/javascript"
     140             :     },
     141             :     {
     142             :       .ext = "jpg",
     143             :       .mime = "image/jpeg"
     144             :     },
     145             :     {
     146             :       .ext = "jpeg",
     147             :       .mime = "image/jpeg"
     148             :     },
     149             :     {
     150             :       .ext = "png",
     151             :       .mime = "image/png"
     152             :     },
     153             :     {
     154             :       .ext = "svg",
     155             :       .mime = "image/svg+xml"
     156             :     },
     157             :     {
     158             :       .ext = NULL,
     159             :       .mime = NULL
     160             :     },
     161             :   };
     162         888 :   struct TALER_MHD_Spa *spa = cls;
     163             :   int fd;
     164             :   struct stat sb;
     165             :   struct MHD_Response *response;
     166             :   char *ext;
     167             :   const char *mime;
     168             :   const char *slash;
     169             :   char *fn;
     170         888 :   const char *cts = NULL;
     171         888 :   enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
     172             : 
     173         888 :   slash = strrchr (dn, '/');
     174         888 :   if (NULL == slash)
     175             :   {
     176           0 :     GNUNET_break (0);
     177           0 :     return GNUNET_SYSERR;
     178             :   }
     179         888 :   fd = open (dn,
     180             :              O_RDONLY);
     181         888 :   if (-1 == fd)
     182             :   {
     183           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     184             :                               "open",
     185             :                               dn);
     186           0 :     return GNUNET_SYSERR;
     187             :   }
     188         888 :   if (0 !=
     189         888 :       fstat (fd,
     190             :              &sb))
     191             :   {
     192           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     193             :                               "open",
     194             :                               dn);
     195           0 :     GNUNET_break (0 == close (fd));
     196           0 :     return GNUNET_SYSERR;
     197             :   }
     198             : 
     199         888 :   fn = GNUNET_strdup (slash + 1);
     200         888 :   ext = strrchr (fn,
     201             :                  '.');
     202         888 :   if (NULL == ext)
     203             :   {
     204          80 :     GNUNET_break (0 == close (fd));
     205          80 :     GNUNET_free (fn);
     206          80 :     return GNUNET_OK;
     207             :   }
     208         808 :   if (0 == strcmp (ext,
     209             :                    ".gz"))
     210             :   {
     211           8 :     cts = "gzip";
     212           8 :     ct = TALER_MHD_CT_GZIP;
     213           8 :     *ext = '\0';
     214           8 :     ext = strrchr (fn, '.');
     215             :   }
     216         808 :   if (0 == strcmp (ext,
     217             :                    ".zstd"))
     218             :   {
     219           8 :     cts = "zstd";
     220           8 :     ct = TALER_MHD_CT_ZSTD;
     221           8 :     *ext = '\0';
     222           8 :     ext = strrchr (fn, '.');
     223             :   }
     224         808 :   if (NULL == ext)
     225             :   {
     226           0 :     GNUNET_break (0 == close (fd));
     227           0 :     GNUNET_free (fn);
     228           0 :     return GNUNET_OK;
     229             :   }
     230         808 :   ext++;
     231             : 
     232         808 :   mime = NULL;
     233        4846 :   for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
     234        4372 :     if (0 == strcasecmp (ext,
     235        4372 :                          mime_map[i].ext))
     236             :     {
     237         334 :       mime = mime_map[i].mime;
     238         334 :       break;
     239             :     }
     240             : 
     241         808 :   response = MHD_create_response_from_fd (
     242         808 :     sb.st_size,
     243             :     fd /* FD now owned by MHD! */);
     244         808 :   if (NULL == response)
     245             :   {
     246           0 :     GNUNET_free (fn);
     247           0 :     return GNUNET_SYSERR;
     248             :   }
     249         824 :   if ( (NULL != cts) &&
     250             :        (MHD_NO ==
     251          16 :         MHD_add_response_header (response,
     252             :                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
     253             :                                  cts)) )
     254             :   {
     255           0 :     GNUNET_break (0);
     256           0 :     MHD_destroy_response (response);
     257           0 :     GNUNET_free (fn);
     258           0 :     return GNUNET_SYSERR;
     259             :   }
     260         808 :   if (NULL != mime)
     261             :   {
     262         334 :     GNUNET_break (MHD_YES ==
     263             :                   MHD_add_response_header (response,
     264             :                                            MHD_HTTP_HEADER_CONTENT_TYPE,
     265             :                                            mime));
     266             :   }
     267             : 
     268             :   {
     269             :     struct WebuiFile *w;
     270             : 
     271         808 :     for (w = spa->webui_head;
     272        4812 :          NULL != w;
     273        4004 :          w = w->next)
     274             :     {
     275        4020 :       if (0 == strcmp (fn,
     276        4020 :                        w->path))
     277          16 :         break;
     278             :     }
     279         808 :     if (NULL == w)
     280             :     {
     281         792 :       w = GNUNET_new (struct WebuiFile);
     282         792 :       w->path = fn;
     283         792 :       GNUNET_CONTAINER_DLL_insert (spa->webui_head,
     284             :                                    spa->webui_tail,
     285             :                                    w);
     286             :     }
     287             :     else
     288             :     {
     289          16 :       GNUNET_free (fn);
     290             :     }
     291         808 :     GNUNET_assert (NULL == w->responses[ct]);
     292         808 :     w->responses[ct] = response;
     293             :   }
     294         808 :   return GNUNET_OK;
     295             : }
     296             : 
     297             : 
     298             : struct TALER_MHD_Spa *
     299          80 : TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd,
     300             :                     const char *dir)
     301             : {
     302             :   struct TALER_MHD_Spa *spa;
     303             :   char *dn;
     304             : 
     305             :   {
     306             :     char *path;
     307             : 
     308          80 :     path = GNUNET_OS_installation_get_path (pd,
     309             :                                             GNUNET_OS_IPK_DATADIR);
     310          80 :     if (NULL == path)
     311             :     {
     312           0 :       GNUNET_break (0);
     313           0 :       return NULL;
     314             :     }
     315          80 :     GNUNET_asprintf (&dn,
     316             :                      "%s%s",
     317             :                      path,
     318             :                      dir);
     319          80 :     GNUNET_free (path);
     320             :   }
     321          80 :   spa = GNUNET_new (struct TALER_MHD_Spa);
     322          80 :   if (-1 ==
     323          80 :       GNUNET_DISK_directory_scan (dn,
     324             :                                   &build_webui,
     325             :                                   spa))
     326             :   {
     327           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     328             :                 "Failed to load WebUI from `%s'\n",
     329             :                 dn);
     330           0 :     GNUNET_free (dn);
     331           0 :     TALER_MHD_spa_free (spa);
     332           0 :     return NULL;
     333             :   }
     334          80 :   GNUNET_free (dn);
     335          80 :   return spa;
     336             : }
     337             : 
     338             : 
     339             : void
     340          80 : TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
     341             : {
     342             :   struct WebuiFile *w;
     343             : 
     344         872 :   while (NULL != (w = spa->webui_head))
     345             :   {
     346         792 :     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
     347             :                                  spa->webui_tail,
     348             :                                  w);
     349         792 :     for (enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
     350        3960 :          ct < TALER_MHD_CT_MAX;
     351        3168 :          ct++)
     352             :     {
     353        3168 :       if (NULL != w->responses[ct])
     354             :       {
     355         808 :         MHD_destroy_response (w->responses[ct]);
     356         808 :         w->responses[ct] = NULL;
     357             :       }
     358             :     }
     359         792 :     GNUNET_free (w->path);
     360         792 :     GNUNET_free (w);
     361             :   }
     362          80 :   GNUNET_free (spa);
     363          80 : }

Generated by: LCOV version 1.16