exchange

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

mhd2_spa.c (9827B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2020--2025 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 mhd2_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_mhd2_lib.h"
     25 
     26 
     27 /**
     28  * Resource from the WebUi.
     29  */
     30 struct WebuiFile
     31 {
     32   /**
     33    * Kept in a DLL.
     34    */
     35   struct WebuiFile *next;
     36 
     37   /**
     38    * Kept in a DLL.
     39    */
     40   struct WebuiFile *prev;
     41 
     42   /**
     43    * Path this resource matches.
     44    */
     45   char *path;
     46 
     47   /**
     48    * SPA resource, compressed.
     49    */
     50   struct MHD_Response *zresponse;
     51 
     52   /**
     53    * SPA resource, vanilla.
     54    */
     55   struct MHD_Response *response;
     56 
     57 };
     58 
     59 
     60 /**
     61  * Resource from the WebUi.
     62  */
     63 struct TALER_MHD2_Spa
     64 {
     65   /**
     66    * Resources of the WebUI, kept in a DLL.
     67    */
     68   struct WebuiFile *webui_head;
     69 
     70   /**
     71    * Resources of the WebUI, kept in a DLL.
     72    */
     73   struct WebuiFile *webui_tail;
     74 };
     75 
     76 
     77 const struct MHD_Action *
     78 TALER_MHD2_spa_handler (const struct TALER_MHD2_Spa *spa,
     79                         struct MHD_Request *request,
     80                         const char *path)
     81 {
     82   struct WebuiFile *w = NULL;
     83 
     84   if ( (NULL == path) ||
     85        (0 == strcmp (path,
     86                      "")) )
     87     path = "index.html";
     88   for (struct WebuiFile *pos = spa->webui_head;
     89        NULL != pos;
     90        pos = pos->next)
     91     if (0 == strcmp (path,
     92                      pos->path))
     93     {
     94       w = pos;
     95       break;
     96     }
     97   if (NULL == w)
     98     return TALER_MHD2_reply_with_error (request,
     99                                         MHD_HTTP_STATUS_NOT_FOUND,
    100                                         TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
    101                                         path);
    102   if ( (MHD_YES ==
    103         TALER_MHD2_can_compress (request)) &&
    104        (NULL != w->zresponse) )
    105     return MHD_action_from_response (request,
    106                                      w->zresponse);
    107   return MHD_action_from_response (request,
    108                                    w->response);
    109 }
    110 
    111 
    112 /**
    113  * Function called on each file to load for the WebUI.
    114  *
    115  * @param cls the `struct TALER_MHD2_Spa *` to build
    116  * @param dn name of the file to load
    117  */
    118 static enum GNUNET_GenericReturnValue
    119 build_webui (void *cls,
    120              const char *dn)
    121 {
    122   static const struct
    123   {
    124     const char *ext;
    125     const char *mime;
    126   } mime_map[] = {
    127     {
    128       .ext = "css",
    129       .mime = "text/css"
    130     },
    131     {
    132       .ext = "html",
    133       .mime = "text/html"
    134     },
    135     {
    136       .ext = "js",
    137       .mime = "text/javascript"
    138     },
    139     {
    140       .ext = "jpg",
    141       .mime = "image/jpeg"
    142     },
    143     {
    144       .ext = "jpeg",
    145       .mime = "image/jpeg"
    146     },
    147     {
    148       .ext = "png",
    149       .mime = "image/png"
    150     },
    151     {
    152       .ext = "svg",
    153       .mime = "image/svg+xml"
    154     },
    155     {
    156       .ext = NULL,
    157       .mime = NULL
    158     },
    159   };
    160   struct TALER_MHD2_Spa *spa = cls;
    161   int fd;
    162   struct stat sb;
    163   struct MHD_Response *zresponse = NULL;
    164   struct MHD_Response *response;
    165   const char *ext;
    166   const char *mime;
    167 
    168   fd = open (dn,
    169              O_RDONLY);
    170   if (-1 == fd)
    171   {
    172     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    173                               "open",
    174                               dn);
    175     return GNUNET_SYSERR;
    176   }
    177   if (0 !=
    178       fstat (fd,
    179              &sb))
    180   {
    181     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    182                               "open",
    183                               dn);
    184     GNUNET_break (0 == close (fd));
    185     return GNUNET_SYSERR;
    186   }
    187 
    188   mime = NULL;
    189   ext = strrchr (dn, '.');
    190   if (NULL == ext)
    191   {
    192     GNUNET_break (0 == close (fd));
    193     return GNUNET_OK;
    194   }
    195   ext++;
    196   for (unsigned int i = 0; NULL != mime_map[i].ext; i++)
    197     if (0 == strcasecmp (ext,
    198                          mime_map[i].ext))
    199     {
    200       mime = mime_map[i].mime;
    201       break;
    202     }
    203 
    204   {
    205     void *in;
    206     ssize_t r;
    207     size_t csize;
    208 
    209     in = GNUNET_malloc_large (sb.st_size);
    210     if (NULL == in)
    211     {
    212       GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR,
    213                            "malloc");
    214       GNUNET_break (0 == close (fd));
    215       return GNUNET_SYSERR;
    216     }
    217     r = read (fd,
    218               in,
    219               sb.st_size);
    220     if ( (-1 == r) ||
    221          (sb.st_size != (size_t) r) )
    222     {
    223       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    224                                 "read",
    225                                 dn);
    226       GNUNET_free (in);
    227       GNUNET_break (0 == close (fd));
    228       return GNUNET_SYSERR;
    229     }
    230     csize = (size_t) r;
    231     if (TALER_MHD2_body_compress (&in,
    232                                   &csize))
    233     {
    234       zresponse = MHD_response_from_buffer (MHD_HTTP_STATUS_OK,
    235                                             csize,
    236                                             in,
    237                                             &free,
    238                                             in);
    239       if (NULL != zresponse)
    240       {
    241         if (MHD_SC_OK !=
    242             MHD_response_add_header (zresponse,
    243                                      MHD_HTTP_HEADER_CONTENT_ENCODING,
    244                                      "deflate"))
    245         {
    246           GNUNET_break (0);
    247           MHD_response_destroy (zresponse);
    248           zresponse = NULL;
    249         }
    250         else if (NULL != mime)
    251           GNUNET_break (MHD_SC_OK ==
    252                         MHD_response_add_header (zresponse,
    253                                                  MHD_HTTP_HEADER_CONTENT_TYPE,
    254                                                  mime));
    255         /* Which representation we return depends on the
    256            Accept-Encoding header of the client, so shared caches
    257            must not serve one variant to a client that asked for
    258            another. */
    259         if (NULL != zresponse)
    260           GNUNET_break (MHD_SC_OK ==
    261                         MHD_response_add_header (zresponse,
    262                                                  MHD_HTTP_HEADER_VARY,
    263                                                  MHD_HTTP_HEADER_ACCEPT_ENCODING));
    264       }
    265     }
    266     else
    267     {
    268       GNUNET_free (in);
    269     }
    270   }
    271 
    272   response = MHD_response_from_fd (MHD_HTTP_STATUS_OK,
    273                                    fd,
    274                                    0LLU,
    275                                    sb.st_size);
    276   if (NULL == response)
    277   {
    278     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    279                               "open",
    280                               dn);
    281     GNUNET_break (0 == close (fd));
    282     if (NULL != zresponse)
    283     {
    284       MHD_response_destroy (zresponse);
    285       zresponse = NULL;
    286     }
    287     return GNUNET_SYSERR;
    288   }
    289   if (NULL != mime)
    290     GNUNET_break (MHD_SC_OK ==
    291                   MHD_response_add_header (response,
    292                                            MHD_HTTP_HEADER_CONTENT_TYPE,
    293                                            mime));
    294   GNUNET_break (MHD_SC_OK ==
    295                 MHD_response_add_header (response,
    296                                          MHD_HTTP_HEADER_VARY,
    297                                          MHD_HTTP_HEADER_ACCEPT_ENCODING));
    298 
    299   {
    300     struct WebuiFile *w;
    301     const char *fn;
    302 
    303     fn = strrchr (dn, '/');
    304     GNUNET_assert (NULL != fn);
    305     w = GNUNET_new (struct WebuiFile);
    306     w->path = GNUNET_strdup (fn + 1);
    307     w->response = response;
    308     w->zresponse = zresponse;
    309     GNUNET_assert (MHD_SC_OK ==
    310                    MHD_RESPONSE_SET_OPTIONS (response,
    311                                              MHD_R_OPTION_REUSABLE (MHD_YES)));
    312     if (NULL != zresponse)
    313       GNUNET_assert (MHD_SC_OK ==
    314                      MHD_RESPONSE_SET_OPTIONS (zresponse,
    315                                                MHD_R_OPTION_REUSABLE (MHD_YES)))
    316       ;
    317     GNUNET_CONTAINER_DLL_insert (spa->webui_head,
    318                                  spa->webui_tail,
    319                                  w);
    320   }
    321   return GNUNET_OK;
    322 }
    323 
    324 
    325 struct TALER_MHD2_Spa *
    326 TALER_MHD2_spa_load (const struct GNUNET_OS_ProjectData *pd,
    327                      const char *dir)
    328 {
    329   struct TALER_MHD2_Spa *spa;
    330   char *dn;
    331 
    332   {
    333     char *path;
    334 
    335     path = GNUNET_OS_installation_get_path (pd,
    336                                             GNUNET_OS_IPK_DATADIR);
    337     if (NULL == path)
    338     {
    339       GNUNET_break (0);
    340       return NULL;
    341     }
    342     GNUNET_asprintf (&dn,
    343                      "%s%s",
    344                      path,
    345                      dir);
    346     GNUNET_free (path);
    347   }
    348   spa = GNUNET_new (struct TALER_MHD2_Spa);
    349   if (-1 ==
    350       GNUNET_DISK_directory_scan (dn,
    351                                   &build_webui,
    352                                   spa))
    353   {
    354     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    355                 "Failed to load WebUI from `%s'\n",
    356                 dn);
    357     GNUNET_free (dn);
    358     TALER_MHD2_spa_free (spa);
    359     return NULL;
    360   }
    361   GNUNET_free (dn);
    362   return spa;
    363 }
    364 
    365 
    366 void
    367 TALER_MHD2_spa_free (struct TALER_MHD2_Spa *spa)
    368 {
    369   struct WebuiFile *w;
    370 
    371   while (NULL != (w = spa->webui_head))
    372   {
    373     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
    374                                  spa->webui_tail,
    375                                  w);
    376     if (NULL != w->response)
    377     {
    378       MHD_response_destroy (w->response);
    379       w->response = NULL;
    380     }
    381     if (NULL != w->zresponse)
    382     {
    383       MHD_response_destroy (w->zresponse);
    384       w->zresponse = NULL;
    385     }
    386     GNUNET_free (w->path);
    387     GNUNET_free (w);
    388   }
    389   GNUNET_free (spa);
    390 }