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 (8227B)


      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 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   {
    204     GNUNET_break (0 == close (fd));
    205     GNUNET_free (fn);
    206     return GNUNET_OK;
    207   }
    208   if (0 == strcmp (ext,
    209                    ".gz"))
    210   {
    211     cts = "gzip";
    212     ct = TALER_MHD_CT_GZIP;
    213     *ext = '\0';
    214     ext = strrchr (fn, '.');
    215   }
    216   if (0 == strcmp (ext,
    217                    ".zstd"))
    218   {
    219     cts = "zstd";
    220     ct = TALER_MHD_CT_ZSTD;
    221     *ext = '\0';
    222     ext = strrchr (fn, '.');
    223   }
    224   if (NULL == ext)
    225   {
    226     GNUNET_break (0 == close (fd));
    227     GNUNET_free (fn);
    228     return GNUNET_OK;
    229   }
    230   ext++;
    231 
    232   mime = NULL;
    233   for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
    234     if (0 == strcasecmp (ext,
    235                          mime_map[i].ext))
    236     {
    237       mime = mime_map[i].mime;
    238       break;
    239     }
    240 
    241   response = MHD_create_response_from_fd (
    242     sb.st_size,
    243     fd /* FD now owned by MHD! */);
    244   if (NULL == response)
    245   {
    246     GNUNET_free (fn);
    247     return GNUNET_SYSERR;
    248   }
    249   if ( (NULL != cts) &&
    250        (MHD_NO ==
    251         MHD_add_response_header (response,
    252                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
    253                                  cts)) )
    254   {
    255     GNUNET_break (0);
    256     MHD_destroy_response (response);
    257     GNUNET_free (fn);
    258     return GNUNET_SYSERR;
    259   }
    260   if (NULL != mime)
    261   {
    262     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     for (w = spa->webui_head;
    272          NULL != w;
    273          w = w->next)
    274     {
    275       if (0 == strcmp (fn,
    276                        w->path))
    277         break;
    278     }
    279     if (NULL == w)
    280     {
    281       w = GNUNET_new (struct WebuiFile);
    282       w->path = fn;
    283       GNUNET_CONTAINER_DLL_insert (spa->webui_head,
    284                                    spa->webui_tail,
    285                                    w);
    286     }
    287     else
    288     {
    289       GNUNET_free (fn);
    290     }
    291     GNUNET_assert (NULL == w->responses[ct]);
    292     w->responses[ct] = response;
    293   }
    294   return GNUNET_OK;
    295 }
    296 
    297 
    298 struct TALER_MHD_Spa *
    299 TALER_MHD_spa_load_dir (const char *dn)
    300 {
    301   struct TALER_MHD_Spa *spa;
    302 
    303   spa = GNUNET_new (struct TALER_MHD_Spa);
    304   if (-1 ==
    305       GNUNET_DISK_directory_scan (dn,
    306                                   &build_webui,
    307                                   spa))
    308   {
    309     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    310                 "Failed to load WebUI from `%s'\n",
    311                 dn);
    312     TALER_MHD_spa_free (spa);
    313     return NULL;
    314   }
    315   return spa;
    316 }
    317 
    318 
    319 struct TALER_MHD_Spa *
    320 TALER_MHD_spa_load (const struct GNUNET_OS_ProjectData *pd,
    321                     const char *dir)
    322 {
    323   struct TALER_MHD_Spa *spa;
    324   char *dn;
    325   char *path;
    326 
    327   path = GNUNET_OS_installation_get_path (pd,
    328                                           GNUNET_OS_IPK_DATADIR);
    329   if (NULL == path)
    330   {
    331     GNUNET_break (0);
    332     return NULL;
    333   }
    334   GNUNET_asprintf (&dn,
    335                    "%s%s",
    336                    path,
    337                    dir);
    338   GNUNET_free (path);
    339   spa = TALER_MHD_spa_load_dir (dn);
    340   GNUNET_free (dn);
    341   return spa;
    342 }
    343 
    344 
    345 void
    346 TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
    347 {
    348   struct WebuiFile *w;
    349 
    350   while (NULL != (w = spa->webui_head))
    351   {
    352     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
    353                                  spa->webui_tail,
    354                                  w);
    355     for (enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
    356          ct < TALER_MHD_CT_MAX;
    357          ct++)
    358     {
    359       if (NULL != w->responses[ct])
    360       {
    361         MHD_destroy_response (w->responses[ct]);
    362         w->responses[ct] = NULL;
    363       }
    364     }
    365     GNUNET_free (w->path);
    366     GNUNET_free (w);
    367   }
    368   GNUNET_free (spa);
    369 }