exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

mhd_spa.c (8222B)


      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 TALER_MHD_spa_handler (const struct TALER_MHD_Spa *spa,
     75                        struct MHD_Connection *connection,
     76                        const char *path)
     77 {
     78   struct WebuiFile *w = NULL;
     79   enum TALER_MHD_CompressionType comp;
     80 
     81   if ( (NULL == path) ||
     82        (0 == strcmp (path,
     83                      "")) )
     84     path = "index.html";
     85   for (struct WebuiFile *pos = spa->webui_head;
     86        NULL != pos;
     87        pos = pos->next)
     88     if (0 == strcmp (path,
     89                      pos->path))
     90     {
     91       w = pos;
     92       break;
     93     }
     94   if ( (NULL == w) ||
     95        (NULL == w->responses[TALER_MHD_CT_NONE]) )
     96     return TALER_MHD_reply_with_error (connection,
     97                                        MHD_HTTP_NOT_FOUND,
     98                                        TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
     99                                        path);
    100   comp = TALER_MHD_can_compress (connection,
    101                                  TALER_MHD_CT_MAX - 1);
    102   GNUNET_assert (comp < TALER_MHD_CT_MAX);
    103   GNUNET_assert (comp >= 0);
    104   if (NULL != w->responses[comp])
    105     return MHD_queue_response (connection,
    106                                MHD_HTTP_OK,
    107                                w->responses[comp]);
    108   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 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   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   const char *cts = NULL;
    171   enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
    172 
    173   slash = strrchr (dn, '/');
    174   if (NULL == slash)
    175   {
    176     GNUNET_break (0);
    177     return GNUNET_SYSERR;
    178   }
    179   fd = open (dn,
    180              O_RDONLY);
    181   if (-1 == fd)
    182   {
    183     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    184                               "open",
    185                               dn);
    186     return GNUNET_SYSERR;
    187   }
    188   if (0 !=
    189       fstat (fd,
    190              &sb))
    191   {
    192     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    193                               "open",
    194                               dn);
    195     GNUNET_break (0 == close (fd));
    196     return GNUNET_SYSERR;
    197   }
    198 
    199   fn = GNUNET_strdup (slash + 1);
    200   ext = strrchr (fn,
    201                  '.');
    202   if (NULL == ext)
    203     goto skip;
    204   if (0 == strcmp (ext,
    205                    ".gz"))
    206   {
    207     cts = "gzip";
    208     ct = TALER_MHD_CT_GZIP;
    209     *ext = '\0';
    210     ext = strrchr (fn, '.');
    211     if (NULL == ext)
    212       goto skip;
    213   }
    214   if (0 == strcmp (ext,
    215                    ".zstd"))
    216   {
    217     cts = "zstd";
    218     ct = TALER_MHD_CT_ZSTD;
    219     *ext = '\0';
    220     ext = strrchr (fn, '.');
    221     if (NULL == ext)
    222       goto skip;
    223   }
    224   ext++;
    225 
    226   mime = NULL;
    227   for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
    228     if (0 == strcasecmp (ext,
    229                          mime_map[i].ext))
    230     {
    231       mime = mime_map[i].mime;
    232       break;
    233     }
    234 
    235   response = MHD_create_response_from_fd (
    236     sb.st_size,
    237     fd /* FD now owned by MHD! */);
    238   if (NULL == response)
    239   {
    240     GNUNET_free (fn);
    241     return GNUNET_SYSERR;
    242   }
    243   if ( (NULL != cts) &&
    244        (MHD_NO ==
    245         MHD_add_response_header (response,
    246                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
    247                                  cts)) )
    248   {
    249     GNUNET_break (0);
    250     MHD_destroy_response (response);
    251     GNUNET_free (fn);
    252     return GNUNET_SYSERR;
    253   }
    254   if (NULL != mime)
    255   {
    256     GNUNET_break (MHD_YES ==
    257                   MHD_add_response_header (response,
    258                                            MHD_HTTP_HEADER_CONTENT_TYPE,
    259                                            mime));
    260   }
    261 
    262   {
    263     struct WebuiFile *w;
    264 
    265     for (w = spa->webui_head;
    266          NULL != w;
    267          w = w->next)
    268     {
    269       if (0 == strcmp (fn,
    270                        w->path))
    271         break;
    272     }
    273     if (NULL == w)
    274     {
    275       w = GNUNET_new (struct WebuiFile);
    276       w->path = fn;
    277       GNUNET_CONTAINER_DLL_insert (spa->webui_head,
    278                                    spa->webui_tail,
    279                                    w);
    280     }
    281     else
    282     {
    283       GNUNET_free (fn);
    284     }
    285     GNUNET_assert (NULL == w->responses[ct]);
    286     w->responses[ct] = response;
    287   }
    288   return GNUNET_OK;
    289 skip:
    290   GNUNET_break (0 == close (fd));
    291   GNUNET_free (fn);
    292   return GNUNET_OK;
    293 }
    294 
    295 
    296 struct TALER_MHD_Spa *
    297 TALER_MHD_spa_load_dir (const char *dn)
    298 {
    299   struct TALER_MHD_Spa *spa;
    300 
    301   spa = GNUNET_new (struct TALER_MHD_Spa);
    302   if (-1 ==
    303       GNUNET_DISK_directory_scan (dn,
    304                                   &build_webui,
    305                                   spa))
    306   {
    307     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    308                 "Failed to load WebUI from `%s'\n",
    309                 dn);
    310     TALER_MHD_spa_free (spa);
    311     return NULL;
    312   }
    313   return spa;
    314 }
    315 
    316 
    317 struct TALER_MHD_Spa *
    318 TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd,
    319                     const char *dir)
    320 {
    321   struct TALER_MHD_Spa *spa;
    322   char *dn;
    323   char *path;
    324 
    325   path = GNUNET_OS_installation_get_path (pd,
    326                                           GNUNET_OS_IPK_DATADIR);
    327   if (NULL == path)
    328   {
    329     GNUNET_break (0);
    330     return NULL;
    331   }
    332   GNUNET_asprintf (&dn,
    333                    "%s%s",
    334                    path,
    335                    dir);
    336   GNUNET_free (path);
    337   spa = TALER_MHD_spa_load_dir (dn);
    338   GNUNET_free (dn);
    339   return spa;
    340 }
    341 
    342 
    343 void
    344 TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
    345 {
    346   struct WebuiFile *w;
    347 
    348   while (NULL != (w = spa->webui_head))
    349   {
    350     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
    351                                  spa->webui_tail,
    352                                  w);
    353     for (enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
    354          ct < TALER_MHD_CT_MAX;
    355          ct++)
    356     {
    357       if (NULL != w->responses[ct])
    358       {
    359         MHD_destroy_response (w->responses[ct]);
    360         w->responses[ct] = NULL;
    361       }
    362     }
    363     GNUNET_free (w->path);
    364     GNUNET_free (w);
    365   }
    366   GNUNET_free (spa);
    367 }