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


      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 "taler/platform.h"
     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         if (NULL != mime)
    251           GNUNET_break (MHD_SC_OK ==
    252                         MHD_response_add_header (zresponse,
    253                                                  MHD_HTTP_HEADER_CONTENT_TYPE,
    254                                                  mime));
    255       }
    256     }
    257     else
    258     {
    259       GNUNET_free (in);
    260     }
    261   }
    262 
    263   response = MHD_response_from_fd (MHD_HTTP_STATUS_OK,
    264                                    fd,
    265                                    0LLU,
    266                                    sb.st_size);
    267   if (NULL == response)
    268   {
    269     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
    270                               "open",
    271                               dn);
    272     GNUNET_break (0 == close (fd));
    273     if (NULL != zresponse)
    274     {
    275       MHD_response_destroy (zresponse);
    276       zresponse = NULL;
    277     }
    278     return GNUNET_SYSERR;
    279   }
    280   if (NULL != mime)
    281     GNUNET_break (MHD_SC_OK ==
    282                   MHD_response_add_header (response,
    283                                            MHD_HTTP_HEADER_CONTENT_TYPE,
    284                                            mime));
    285 
    286   {
    287     struct WebuiFile *w;
    288     const char *fn;
    289 
    290     fn = strrchr (dn, '/');
    291     GNUNET_assert (NULL != fn);
    292     w = GNUNET_new (struct WebuiFile);
    293     w->path = GNUNET_strdup (fn + 1);
    294     w->response = response;
    295     w->zresponse = zresponse;
    296     GNUNET_assert (MHD_SC_OK ==
    297                    MHD_RESPONSE_SET_OPTIONS (response,
    298                                              MHD_R_OPTION_REUSABLE (MHD_YES)));
    299     if (NULL != zresponse)
    300       GNUNET_assert (MHD_SC_OK ==
    301                      MHD_RESPONSE_SET_OPTIONS (zresponse,
    302                                                MHD_R_OPTION_REUSABLE (MHD_YES)))
    303       ;
    304     GNUNET_CONTAINER_DLL_insert (spa->webui_head,
    305                                  spa->webui_tail,
    306                                  w);
    307   }
    308   return GNUNET_OK;
    309 }
    310 
    311 
    312 struct TALER_MHD2_Spa *
    313 TALER_MHD2_spa_load (const struct GNUNET_OS_ProjectData *pd,
    314                      const char *dir)
    315 {
    316   struct TALER_MHD2_Spa *spa;
    317   char *dn;
    318 
    319   {
    320     char *path;
    321 
    322     path = GNUNET_OS_installation_get_path (pd,
    323                                             GNUNET_OS_IPK_DATADIR);
    324     if (NULL == path)
    325     {
    326       GNUNET_break (0);
    327       return NULL;
    328     }
    329     GNUNET_asprintf (&dn,
    330                      "%s%s",
    331                      path,
    332                      dir);
    333     GNUNET_free (path);
    334   }
    335   spa = GNUNET_new (struct TALER_MHD2_Spa);
    336   if (-1 ==
    337       GNUNET_DISK_directory_scan (dn,
    338                                   &build_webui,
    339                                   spa))
    340   {
    341     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    342                 "Failed to load WebUI from `%s'\n",
    343                 dn);
    344     GNUNET_free (dn);
    345     TALER_MHD2_spa_free (spa);
    346     return NULL;
    347   }
    348   GNUNET_free (dn);
    349   return spa;
    350 }
    351 
    352 
    353 void
    354 TALER_MHD2_spa_free (struct TALER_MHD2_Spa *spa)
    355 {
    356   struct WebuiFile *w;
    357 
    358   while (NULL != (w = spa->webui_head))
    359   {
    360     GNUNET_CONTAINER_DLL_remove (spa->webui_head,
    361                                  spa->webui_tail,
    362                                  w);
    363     if (NULL != w->response)
    364     {
    365       MHD_response_destroy (w->response);
    366       w->response = NULL;
    367     }
    368     if (NULL != w->zresponse)
    369     {
    370       MHD_response_destroy (w->zresponse);
    371       w->zresponse = NULL;
    372     }
    373     GNUNET_free (w->path);
    374     GNUNET_free (w);
    375   }
    376   GNUNET_free (spa);
    377 }