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


      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     /* MHD did not take ownership of the FD after all */
    241     GNUNET_break (0 == close (fd));
    242     GNUNET_free (fn);
    243     return GNUNET_SYSERR;
    244   }
    245   if ( (NULL != cts) &&
    246        (MHD_NO ==
    247         MHD_add_response_header (response,
    248                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
    249                                  cts)) )
    250   {
    251     GNUNET_break (0);
    252     MHD_destroy_response (response);
    253     GNUNET_free (fn);
    254     return GNUNET_SYSERR;
    255   }
    256   if (NULL != mime)
    257   {
    258     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   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     for (w = spa->webui_head;
    275          NULL != w;
    276          w = w->next)
    277     {
    278       if (0 == strcmp (fn,
    279                        w->path))
    280         break;
    281     }
    282     if (NULL == w)
    283     {
    284       w = GNUNET_new (struct WebuiFile);
    285       w->path = fn;
    286       GNUNET_CONTAINER_DLL_insert (spa->webui_head,
    287                                    spa->webui_tail,
    288                                    w);
    289     }
    290     else
    291     {
    292       GNUNET_free (fn);
    293     }
    294     GNUNET_assert (NULL == w->responses[ct]);
    295     w->responses[ct] = response;
    296   }
    297   return GNUNET_OK;
    298 skip:
    299   GNUNET_break (0 == close (fd));
    300   GNUNET_free (fn);
    301   return GNUNET_OK;
    302 }
    303 
    304 
    305 struct TALER_MHD_Spa *
    306 TALER_MHD_spa_load_dir (const char *dn)
    307 {
    308   struct TALER_MHD_Spa *spa;
    309 
    310   spa = GNUNET_new (struct TALER_MHD_Spa);
    311   if (-1 ==
    312       GNUNET_DISK_directory_scan (dn,
    313                                   &build_webui,
    314                                   spa))
    315   {
    316     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    317                 "Failed to load WebUI from `%s'\n",
    318                 dn);
    319     TALER_MHD_spa_free (spa);
    320     return NULL;
    321   }
    322   return spa;
    323 }
    324 
    325 
    326 struct TALER_MHD_Spa *
    327 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   path = GNUNET_OS_installation_get_path (pd,
    335                                           GNUNET_OS_IPK_DATADIR);
    336   if (NULL == path)
    337   {
    338     GNUNET_break (0);
    339     return NULL;
    340   }
    341   GNUNET_asprintf (&dn,
    342                    "%s%s",
    343                    path,
    344                    dir);
    345   GNUNET_free (path);
    346   spa = TALER_MHD_spa_load_dir (dn);
    347   GNUNET_free (dn);
    348   return spa;
    349 }
    350 
    351 
    352 void
    353 TALER_MHD_spa_free (struct TALER_MHD_Spa *spa)
    354 {
    355   struct WebuiFile *w;
    356 
    357   while (NULL != (w = spa->webui_head))
    358   {
    359     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
    360                                  spa->webui_tail,
    361                                  w);
    362     for (enum TALER_MHD_CompressionType ct = TALER_MHD_CT_NONE;
    363          ct < TALER_MHD_CT_MAX;
    364          ct++)
    365     {
    366       if (NULL != w->responses[ct])
    367       {
    368         MHD_destroy_response (w->responses[ct]);
    369         w->responses[ct] = NULL;
    370       }
    371     }
    372     GNUNET_free (w->path);
    373     GNUNET_free (w);
    374   }
    375   GNUNET_free (spa);
    376 }