LCOV - code coverage report
Current view: top level - mhd - mhd_spa.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 50.8 % 126 64
Test Date: 2026-09-23 23:08:06 Functions: 80.0 % 5 4

            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 "platform.h"  /* UNNECESSARY? */
      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              : enum 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           41 : 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           41 :   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           41 :   const char *cts = NULL;
     171           41 :   enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
     172              : 
     173           41 :   slash = strrchr (dn, '/');
     174           41 :   if (NULL == slash)
     175              :   {
     176            0 :     GNUNET_break (0);
     177            0 :     return GNUNET_SYSERR;
     178              :   }
     179           41 :   fd = open (dn,
     180              :              O_RDONLY);
     181           41 :   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           41 :   if (0 !=
     189           41 :       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           41 :   fn = GNUNET_strdup (slash + 1);
     200           41 :   ext = strrchr (fn,
     201              :                  '.');
     202           41 :   if (NULL == ext)
     203            0 :     goto skip;
     204           41 :   if (0 == strcmp (ext,
     205              :                    ".gz"))
     206              :   {
     207            0 :     cts = "gzip";
     208            0 :     ct = TALER_MHD_CT_GZIP;
     209            0 :     *ext = '\0';
     210            0 :     ext = strrchr (fn, '.');
     211            0 :     if (NULL == ext)
     212            0 :       goto skip;
     213              :   }
     214           41 :   if (0 == strcmp (ext,
     215              :                    ".zstd"))
     216              :   {
     217            0 :     cts = "zstd";
     218            0 :     ct = TALER_MHD_CT_ZSTD;
     219            0 :     *ext = '\0';
     220            0 :     ext = strrchr (fn, '.');
     221            0 :     if (NULL == ext)
     222            0 :       goto skip;
     223              :   }
     224           41 :   ext++;
     225              : 
     226           41 :   mime = NULL;
     227           82 :   for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
     228           82 :     if (0 == strcasecmp (ext,
     229           82 :                          mime_map[i].ext))
     230              :     {
     231           41 :       mime = mime_map[i].mime;
     232           41 :       break;
     233              :     }
     234              : 
     235           41 :   response = MHD_create_response_from_fd (
     236           41 :     sb.st_size,
     237              :     fd /* FD now owned by MHD! */);
     238           41 :   if (NULL == response)
     239              :   {
     240              :     /* MHD did not take ownership of the FD after all */
     241            0 :     GNUNET_break (0 == close (fd));
     242            0 :     GNUNET_free (fn);
     243            0 :     return GNUNET_SYSERR;
     244              :   }
     245           41 :   if ( (NULL != cts) &&
     246              :        (MHD_NO ==
     247            0 :         MHD_add_response_header (response,
     248              :                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
     249              :                                  cts)) )
     250              :   {
     251            0 :     GNUNET_break (0);
     252            0 :     MHD_destroy_response (response);
     253            0 :     GNUNET_free (fn);
     254            0 :     return GNUNET_SYSERR;
     255              :   }
     256           41 :   if (NULL != mime)
     257              :   {
     258           41 :     GNUNET_break (MHD_YES ==
     259              :                   MHD_add_response_header (response,
     260              :                                            MHD_HTTP_HEADER_CONTENT_TYPE,
     261              :                                            mime));
     262              :   }
     263              :   /* Which representation we return depends on the Accept-Encoding
     264              :      header of the client, so shared caches must not serve one
     265              :      variant to a client that asked for another. */
     266           41 :   GNUNET_break (MHD_YES ==
     267              :                 MHD_add_response_header (response,
     268              :                                          MHD_HTTP_HEADER_VARY,
     269              :                                          MHD_HTTP_HEADER_ACCEPT_ENCODING));
     270              : 
     271              :   {
     272              :     struct WebuiFile *w;
     273              : 
     274           41 :     for (w = spa->webui_head;
     275           41 :          NULL != w;
     276            0 :          w = w->next)
     277              :     {
     278            0 :       if (0 == strcmp (fn,
     279            0 :                        w->path))
     280            0 :         break;
     281              :     }
     282           41 :     if (NULL == w)
     283              :     {
     284           41 :       w = GNUNET_new (struct WebuiFile);
     285           41 :       w->path = fn;
     286           41 :       GNUNET_CONTAINER_DLL_insert (spa->webui_head,
     287              :                                    spa->webui_tail,
     288              :                                    w);
     289              :     }
     290              :     else
     291              :     {
     292            0 :       GNUNET_free (fn);
     293              :     }
     294           41 :     GNUNET_assert (NULL == w->responses[ct]);
     295           41 :     w->responses[ct] = response;
     296              :   }
     297           41 :   return GNUNET_OK;
     298            0 : skip:
     299            0 :   GNUNET_break (0 == close (fd));
     300            0 :   GNUNET_free (fn);
     301            0 :   return GNUNET_OK;
     302              : }
     303              : 
     304              : 
     305              : struct TALER_MHD_Spa *
     306           41 : TALER_MHD_spa_load_dir (const char *dn)
     307              : {
     308              :   struct TALER_MHD_Spa *spa;
     309              : 
     310           41 :   spa = GNUNET_new (struct TALER_MHD_Spa);
     311           41 :   if (-1 ==
     312           41 :       GNUNET_DISK_directory_scan (dn,
     313              :                                   &build_webui,
     314              :                                   spa))
     315              :   {
     316            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     317              :                 "Failed to load WebUI from `%s'\n",
     318              :                 dn);
     319            0 :     TALER_MHD_spa_free (spa);
     320            0 :     return NULL;
     321              :   }
     322           41 :   return spa;
     323              : }
     324              : 
     325              : 
     326              : struct TALER_MHD_Spa *
     327           41 : TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd,
     328              :                     const char *dir)
     329              : {
     330              :   struct TALER_MHD_Spa *spa;
     331              :   char *dn;
     332              :   char *path;
     333              : 
     334           41 :   path = GNUNET_OS_installation_get_path (pd,
     335              :                                           GNUNET_OS_IPK_DATADIR);
     336           41 :   if (NULL == path)
     337              :   {
     338            0 :     GNUNET_break (0);
     339            0 :     return NULL;
     340              :   }
     341           41 :   GNUNET_asprintf (&dn,
     342              :                    "%s%s",
     343              :                    path,
     344              :                    dir);
     345           41 :   GNUNET_free (path);
     346           41 :   spa = TALER_MHD_spa_load_dir (dn);
     347           41 :   GNUNET_free (dn);
     348           41 :   return spa;
     349              : }
     350              : 
     351              : 
     352              : void
     353           41 : TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
     354              : {
     355              :   struct WebuiFile *w;
     356              : 
     357           82 :   while (NULL != (w = spa->webui_head))
     358              :   {
     359           41 :     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
     360              :                                  spa->webui_tail,
     361              :                                  w);
     362           41 :     for (enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
     363          205 :          ct < TALER_MHD_CT_MAX;
     364          164 :          ct++)
     365              :     {
     366          164 :       if (NULL != w->responses[ct])
     367              :       {
     368           41 :         MHD_destroy_response (w->responses[ct]);
     369           41 :         w->responses[ct] = NULL;
     370              :       }
     371              :     }
     372           41 :     GNUNET_free (w->path);
     373           41 :     GNUNET_free (w);
     374              :   }
     375           41 :   GNUNET_free (spa);
     376           41 : }
        

Generated by: LCOV version 2.0-1