LCOV - code coverage report
Current view: top level - mhd - mhd_spa.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 69 115 60.0 %
Date: 2025-06-05 21:03:14 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 "platform.h"
      22             : #include <gnunet/gnunet_util_lib.h>
      23             : #include "taler_util.h"
      24             : #include "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, compressed.
      50             :    */
      51             :   struct MHD_Response *zresponse;
      52             : 
      53             :   /**
      54             :    * SPA resource, vanilla.
      55             :    */
      56             :   struct MHD_Response *response;
      57             : 
      58             : };
      59             : 
      60             : 
      61             : /**
      62             :  * Resource from the WebUi.
      63             :  */
      64             : struct TALER_MHD_Spa
      65             : {
      66             :   /**
      67             :    * Resources of the WebUI, kept in a DLL.
      68             :    */
      69             :   struct WebuiFile *webui_head;
      70             : 
      71             :   /**
      72             :    * Resources of the WebUI, kept in a DLL.
      73             :    */
      74             :   struct WebuiFile *webui_tail;
      75             : };
      76             : 
      77             : 
      78             : MHD_RESULT
      79           0 : TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa,
      80             :                        struct MHD_Connection *connection,
      81             :                        const char *path)
      82             : {
      83           0 :   struct WebuiFile *w = NULL;
      84             : 
      85           0 :   if ( (NULL == path) ||
      86           0 :        (0 == strcmp (path,
      87             :                      "")) )
      88           0 :     path = "index.html";
      89           0 :   for (struct WebuiFile *pos = spa->webui_head;
      90           0 :        NULL != pos;
      91           0 :        pos = pos->next)
      92           0 :     if (0 == strcmp (path,
      93           0 :                      pos->path))
      94             :     {
      95           0 :       w = pos;
      96           0 :       break;
      97             :     }
      98           0 :   if (NULL == w)
      99           0 :     return TALER_MHD_reply_with_error (connection,
     100             :                                        MHD_HTTP_NOT_FOUND,
     101             :                                        TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
     102             :                                        path);
     103           0 :   if ( (MHD_YES ==
     104           0 :         TALER_MHD_can_compress (connection)) &&
     105           0 :        (NULL != w->zresponse) )
     106           0 :     return MHD_queue_response (connection,
     107             :                                MHD_HTTP_OK,
     108             :                                w->zresponse);
     109           0 :   return MHD_queue_response (connection,
     110             :                              MHD_HTTP_OK,
     111             :                              w->response);
     112             : }
     113             : 
     114             : 
     115             : /**
     116             :  * Function called on each file to load for the WebUI.
     117             :  *
     118             :  * @param cls the `struct TALER_MHD_Spa *` to build
     119             :  * @param dn name of the file to load
     120             :  */
     121             : static enum GNUNET_GenericReturnValue
     122         840 : build_webui (void *cls,
     123             :              const char *dn)
     124             : {
     125             :   static const struct
     126             :   {
     127             :     const char *ext;
     128             :     const char *mime;
     129             :   } mime_map[] = {
     130             :     {
     131             :       .ext = "css",
     132             :       .mime = "text/css"
     133             :     },
     134             :     {
     135             :       .ext = "html",
     136             :       .mime = "text/html"
     137             :     },
     138             :     {
     139             :       .ext = "js",
     140             :       .mime = "text/javascript"
     141             :     },
     142             :     {
     143             :       .ext = "jpg",
     144             :       .mime = "image/jpeg"
     145             :     },
     146             :     {
     147             :       .ext = "jpeg",
     148             :       .mime = "image/jpeg"
     149             :     },
     150             :     {
     151             :       .ext = "png",
     152             :       .mime = "image/png"
     153             :     },
     154             :     {
     155             :       .ext = "svg",
     156             :       .mime = "image/svg+xml"
     157             :     },
     158             :     {
     159             :       .ext = NULL,
     160             :       .mime = NULL
     161             :     },
     162             :   };
     163         840 :   struct TALER_MHD_Spa *spa = cls;
     164             :   int fd;
     165             :   struct stat sb;
     166         840 :   struct MHD_Response *zresponse = NULL;
     167             :   struct MHD_Response *response;
     168             :   const char *ext;
     169             :   const char *mime;
     170             : 
     171         840 :   fd = open (dn,
     172             :              O_RDONLY);
     173         840 :   if (-1 == fd)
     174             :   {
     175           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     176             :                               "open",
     177             :                               dn);
     178           0 :     return GNUNET_SYSERR;
     179             :   }
     180         840 :   if (0 !=
     181         840 :       fstat (fd,
     182             :              &sb))
     183             :   {
     184           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     185             :                               "open",
     186             :                               dn);
     187           0 :     GNUNET_break (0 == close (fd));
     188           0 :     return GNUNET_SYSERR;
     189             :   }
     190             : 
     191         840 :   mime = NULL;
     192         840 :   ext = strrchr (dn, '.');
     193         840 :   if (NULL == ext)
     194             :   {
     195          78 :     GNUNET_break (0 == close (fd));
     196          78 :     return GNUNET_OK;
     197             :   }
     198         762 :   ext++;
     199        4620 :   for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
     200        4164 :     if (0 == strcasecmp (ext,
     201        4164 :                          mime_map[i].ext))
     202             :     {
     203         306 :       mime = mime_map[i].mime;
     204         306 :       break;
     205             :     }
     206             : 
     207             :   {
     208             :     void *in;
     209             :     ssize_t r;
     210             :     size_t csize;
     211             : 
     212         762 :     in = GNUNET_malloc_large (sb.st_size);
     213         762 :     if (NULL == in)
     214             :     {
     215           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
     216             :                            "malloc");
     217           0 :       GNUNET_break (0 == close (fd));
     218           0 :       return GNUNET_SYSERR;
     219             :     }
     220         762 :     r = read (fd,
     221             :               in,
     222         762 :               sb.st_size);
     223         762 :     if ( (-1 == r) ||
     224         762 :          (sb.st_size != (size_t) r) )
     225             :     {
     226           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     227             :                                 "read",
     228             :                                 dn);
     229           0 :       GNUNET_free (in);
     230           0 :       GNUNET_break (0 == close (fd));
     231           0 :       return GNUNET_SYSERR;
     232             :     }
     233         762 :     csize = (size_t) r;
     234         762 :     if (MHD_YES ==
     235         762 :         TALER_MHD_body_compress (&in,
     236             :                                  &csize))
     237             :     {
     238         663 :       zresponse = MHD_create_response_from_buffer (csize,
     239             :                                                    in,
     240             :                                                    MHD_RESPMEM_MUST_FREE);
     241         663 :       if (NULL != zresponse)
     242             :       {
     243         663 :         if (MHD_NO ==
     244         663 :             MHD_add_response_header (zresponse,
     245             :                                      MHD_HTTP_HEADER_CONTENT_ENCODING,
     246             :                                      "deflate"))
     247             :         {
     248           0 :           GNUNET_break (0);
     249           0 :           MHD_destroy_response (zresponse);
     250           0 :           zresponse = NULL;
     251             :         }
     252         663 :         if (NULL != mime)
     253         306 :           GNUNET_break (MHD_YES ==
     254             :                         MHD_add_response_header (zresponse,
     255             :                                                  MHD_HTTP_HEADER_CONTENT_TYPE,
     256             :                                                  mime));
     257             :       }
     258             :     }
     259             :     else
     260             :     {
     261          99 :       GNUNET_free (in);
     262             :     }
     263             :   }
     264             : 
     265         762 :   response = MHD_create_response_from_fd (sb.st_size,
     266             :                                           fd);
     267         762 :   if (NULL == response)
     268             :   {
     269           0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     270             :                               "open",
     271             :                               dn);
     272           0 :     GNUNET_break (0 == close (fd));
     273           0 :     if (NULL != zresponse)
     274             :     {
     275           0 :       MHD_destroy_response (zresponse);
     276           0 :       zresponse = NULL;
     277             :     }
     278           0 :     return GNUNET_SYSERR;
     279             :   }
     280         762 :   if (NULL != mime)
     281         306 :     GNUNET_break (MHD_YES ==
     282             :                   MHD_add_response_header (response,
     283             :                                            MHD_HTTP_HEADER_CONTENT_TYPE,
     284             :                                            mime));
     285             : 
     286             :   {
     287             :     struct WebuiFile *w;
     288             :     const char *fn;
     289             : 
     290         762 :     fn = strrchr (dn, '/');
     291         762 :     GNUNET_assert (NULL != fn);
     292         762 :     w = GNUNET_new (struct WebuiFile);
     293         762 :     w->path = GNUNET_strdup (fn + 1);
     294         762 :     w->response = response;
     295         762 :     w->zresponse = zresponse;
     296         762 :     GNUNET_CONTAINER_DLL_insert (spa->webui_head,
     297             :                                  spa->webui_tail,
     298             :                                  w);
     299             :   }
     300         762 :   return GNUNET_OK;
     301             : }
     302             : 
     303             : 
     304             : struct TALER_MHD_Spa *
     305          78 : TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd,
     306             :                     const char *dir)
     307             : {
     308             :   struct TALER_MHD_Spa *spa;
     309             :   char *dn;
     310             : 
     311             :   {
     312             :     char *path;
     313             : 
     314          78 :     path = GNUNET_OS_installation_get_path (pd,
     315             :                                             GNUNET_OS_IPK_DATADIR);
     316          78 :     if (NULL == path)
     317             :     {
     318           0 :       GNUNET_break (0);
     319           0 :       return NULL;
     320             :     }
     321          78 :     GNUNET_asprintf (&dn,
     322             :                      "%s%s",
     323             :                      path,
     324             :                      dir);
     325          78 :     GNUNET_free (path);
     326             :   }
     327          78 :   spa = GNUNET_new (struct TALER_MHD_Spa);
     328          78 :   if (-1 ==
     329          78 :       GNUNET_DISK_directory_scan (dn,
     330             :                                   &build_webui,
     331             :                                   spa))
     332             :   {
     333           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     334             :                 "Failed to load WebUI from `%s'\n",
     335             :                 dn);
     336           0 :     GNUNET_free (dn);
     337           0 :     TALER_MHD_spa_free (spa);
     338           0 :     return NULL;
     339             :   }
     340          78 :   GNUNET_free (dn);
     341          78 :   return spa;
     342             : }
     343             : 
     344             : 
     345             : void
     346          78 : TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
     347             : {
     348             :   struct WebuiFile *w;
     349             : 
     350         840 :   while (NULL != (w = spa->webui_head))
     351             :   {
     352         762 :     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
     353             :                                  spa->webui_tail,
     354             :                                  w);
     355         762 :     if (NULL != w->response)
     356             :     {
     357         762 :       MHD_destroy_response (w->response);
     358         762 :       w->response = NULL;
     359             :     }
     360         762 :     if (NULL != w->zresponse)
     361             :     {
     362         663 :       MHD_destroy_response (w->zresponse);
     363         663 :       w->zresponse = NULL;
     364             :     }
     365         762 :     GNUNET_free (w->path);
     366         762 :     GNUNET_free (w);
     367             :   }
     368          78 :   GNUNET_free (spa);
     369          78 : }

Generated by: LCOV version 1.16