LCOV - code coverage report
Current view: top level - backend - taler-merchant-httpd_statics.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 4.2 % 95 4
Test Date: 2025-11-06 19:31:41 Functions: 20.0 % 5 1

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2020 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 MERCHANTABILITY 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 taler-merchant-httpd_statics.c
      18              :  * @brief logic to load and return static resource files by client language preference
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include <gnunet/gnunet_util_lib.h>
      23              : #include "taler_merchant_util.h"
      24              : #include <taler/taler_util.h>
      25              : #include <taler/taler_mhd_lib.h>
      26              : #include <taler/taler_templating_lib.h>
      27              : #include "taler-merchant-httpd_statics.h"
      28              : #include <gnunet/gnunet_mhd_compat.h>
      29              : 
      30              : 
      31              : /**
      32              :  * Entry in a key-value array we use to cache templates.
      33              :  */
      34              : struct TVE
      35              : {
      36              :   /**
      37              :    * A name, used as the key. NULL for the last entry.
      38              :    */
      39              :   char *name;
      40              : 
      41              :   /**
      42              :    * Language the template is in.
      43              :    */
      44              :   char *lang;
      45              : 
      46              :   /**
      47              :    * Pre-built reply.
      48              :    */
      49              :   struct MHD_Response *reply;
      50              : 
      51              : };
      52              : 
      53              : 
      54              : /**
      55              :  * Array of templates loaded into RAM.
      56              :  */
      57              : static struct TVE *loaded;
      58              : 
      59              : /**
      60              :  * Length of the #loaded array.
      61              :  */
      62              : static unsigned int loaded_length;
      63              : 
      64              : 
      65              : /**
      66              :  * Load Mustach template into memory.  Note that we intentionally cache
      67              :  * failures, that is if we ever failed to load a template, we will never try
      68              :  * again.
      69              :  *
      70              :  * @param connection the connection we act upon
      71              :  * @param name name of the template file to load
      72              :  *        (MUST be a 'static' string in memory!)
      73              :  * @return NULL on error, otherwise the template
      74              :  */
      75              : static const struct TVE *
      76            0 : lookup_file (struct MHD_Connection *connection,
      77              :              const char *name)
      78              : {
      79            0 :   double best_q = 0.0;
      80            0 :   struct TVE *best = NULL;
      81              :   const char *lang;
      82              : 
      83            0 :   lang = MHD_lookup_connection_value (connection,
      84              :                                       MHD_HEADER_KIND,
      85              :                                       MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
      86            0 :   if (NULL == lang)
      87            0 :     lang = "en";
      88              :   /* find best match by language */
      89            0 :   for (unsigned int i = 0; i<loaded_length; i++)
      90              :   {
      91              :     double q;
      92              : 
      93            0 :     if (0 != strcmp (loaded[i].name,
      94              :                      name))
      95            0 :       continue; /* does not match by name */
      96            0 :     if (NULL == loaded[i].lang) /* no language == always best match */
      97            0 :       return &loaded[i];
      98            0 :     q = TALER_pattern_matches (lang,
      99            0 :                                loaded[i].lang);
     100            0 :     if (q < best_q)
     101            0 :       continue;
     102            0 :     best_q = q;
     103            0 :     best = &loaded[i];
     104              :   }
     105            0 :   if (NULL == best)
     106              :   {
     107            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     108              :                 "No static file found for `%s'\n",
     109              :                 name);
     110            0 :     return NULL;
     111              :   }
     112            0 :   return best;
     113              : }
     114              : 
     115              : 
     116              : MHD_RESULT
     117            0 : TMH_return_static (const struct TMH_RequestHandler *rh,
     118              :                    struct MHD_Connection *connection,
     119              :                    struct TMH_HandlerContext *hc)
     120              : {
     121              :   const struct TVE *tmpl;
     122              : 
     123            0 :   tmpl = lookup_file (connection,
     124            0 :                       hc->infix);
     125            0 :   if (NULL == tmpl)
     126              :   {
     127            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     128              :                 "Failed to load static file `%s'\n",
     129              :                 hc->infix);
     130            0 :     return TALER_MHD_reply_with_error (connection,
     131              :                                        MHD_HTTP_NOT_FOUND,
     132              :                                        TALER_EC_GENERIC_ENDPOINT_UNKNOWN,
     133            0 :                                        hc->infix);
     134              :   }
     135              : 
     136            0 :   return MHD_queue_response (connection,
     137              :                              MHD_HTTP_OK,
     138            0 :                              tmpl->reply);
     139              : }
     140              : 
     141              : 
     142              : /**
     143              :  * Function called with a static file's filename.
     144              :  *
     145              :  * @param cls closure
     146              :  * @param filename complete filename (absolute path)
     147              :  * @return #GNUNET_OK to continue to iterate,
     148              :  *  #GNUNET_NO to stop iteration with no error,
     149              :  *  #GNUNET_SYSERR to abort iteration with error!
     150              :  */
     151              : static enum GNUNET_GenericReturnValue
     152            0 : load_static_file (void *cls,
     153              :                   const char *filename)
     154              : {
     155              :   char *lang;
     156              :   char *end;
     157              :   int fd;
     158              :   struct stat sb;
     159              :   const char *name;
     160              :   struct MHD_Response *reply;
     161              : 
     162            0 :   if ('.' == filename[0])
     163            0 :     return GNUNET_OK;
     164            0 :   name = strrchr (filename,
     165              :                   '/');
     166            0 :   if (NULL == name)
     167            0 :     name = filename;
     168              :   else
     169            0 :     name++;
     170            0 :   lang = strchr (name,
     171              :                  '.');
     172            0 :   if (NULL == lang)
     173            0 :     return GNUNET_OK; /* name must include _some_ extension */
     174            0 :   lang++;
     175            0 :   end = strchr (lang,
     176              :                 '.');
     177            0 :   if (NULL == end)
     178              :   {
     179              :     /* language was not present, we ONLY have the extension */
     180            0 :     end = lang - 1;
     181            0 :     lang = NULL;
     182              :   }
     183              :   /* finally open template */
     184            0 :   fd = open (filename,
     185              :              O_RDONLY);
     186            0 :   if (-1 == fd)
     187              :   {
     188            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     189              :                               "open",
     190              :                               filename);
     191            0 :     return GNUNET_SYSERR;
     192              :   }
     193            0 :   if (0 !=
     194            0 :       fstat (fd,
     195              :              &sb))
     196              :   {
     197            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     198              :                               "open",
     199              :                               filename);
     200            0 :     GNUNET_break (0 == close (fd));
     201            0 :     return GNUNET_OK;
     202              :   }
     203              : 
     204            0 :   reply = MHD_create_response_from_fd (sb.st_size,
     205              :                                        fd);
     206            0 :   if (NULL == reply)
     207              :   {
     208            0 :     GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR,
     209              :                               "open",
     210              :                               filename);
     211            0 :     GNUNET_break (0 == close (fd));
     212            0 :     return GNUNET_OK;
     213              :   }
     214              : 
     215              :   {
     216              :     static struct MimeMap
     217              :     {
     218              :       const char *ext;
     219              :       const char *mime;
     220              :     } mm[] = {
     221              :       { .ext = ".css", .mime = "text/css" },
     222              :       { .ext = ".js", .mime = "text/javascript" },
     223              :       { .ext = ".html", .mime = "text/html" },
     224              :       { .ext = ".htm", .mime = "text/html" },
     225              :       { .ext = ".txt", .mime = "text/plain" },
     226              :       { .ext = ".pdf", .mime = "application/pdf" },
     227              :       { .ext = ".jpg", .mime = "image/jpeg" },
     228              :       { .ext = ".jpeg", .mime = "image/jpeg" },
     229              :       { .ext = ".png", .mime = "image/png" },
     230              :       { .ext = ".apng", .mime = "image/apng" },
     231              :       { .ext = ".gif", .mime = "image/gif" },
     232              :       { .ext = ".svg", .mime = "image/svg+xml" },
     233              :       { .ext = ".tiff", .mime = "image/tiff" },
     234              :       { .ext = ".ico", .mime = "image/x-icon" },
     235              :       { .ext = ".bmp", .mime = "image/bmp" },
     236              :       { .ext = ".epub", .mime = "application/epub+zip" },
     237              :       { .ext = ".xml", .mime = "text/xml" },
     238              :       { .ext = NULL, .mime = NULL }
     239              :     };
     240              :     const char *mime;
     241              : 
     242            0 :     mime = NULL;
     243            0 :     for (unsigned int i = 0; NULL != mm[i].ext; i++)
     244            0 :       if (0 == strcasecmp (mm[i].ext,
     245              :                            end))
     246              :       {
     247            0 :         mime = mm[i].mime;
     248            0 :         break;
     249              :       }
     250            0 :     if (NULL != mime)
     251            0 :       GNUNET_break (MHD_NO !=
     252              :                     MHD_add_response_header (reply,
     253              :                                              MHD_HTTP_HEADER_CONTENT_TYPE,
     254              :                                              mime));
     255              :   }
     256              : 
     257            0 :   GNUNET_array_grow (loaded,
     258              :                      loaded_length,
     259              :                      loaded_length + 1);
     260            0 :   if (NULL != lang)
     261              :   {
     262            0 :     GNUNET_asprintf (&loaded[loaded_length - 1].name,
     263              :                      "%.*s%s",
     264            0 :                      (int) (lang - name) - 1,
     265              :                      name,
     266              :                      end);
     267            0 :     loaded[loaded_length - 1].lang = GNUNET_strndup (lang,
     268              :                                                      end - lang);
     269              :   }
     270              :   else
     271              :   {
     272            0 :     loaded[loaded_length - 1].name = GNUNET_strdup (name);
     273              :   }
     274            0 :   loaded[loaded_length - 1].reply = reply;
     275            0 :   return GNUNET_OK;
     276              : }
     277              : 
     278              : 
     279              : /**
     280              :  * Preload static files.
     281              :  */
     282              : enum GNUNET_GenericReturnValue
     283            0 : TMH_statics_init ()
     284              : {
     285              :   char *dn;
     286              :   int ret;
     287              : 
     288              :   {
     289              :     char *path;
     290              : 
     291            0 :     path = GNUNET_OS_installation_get_path (TALER_MERCHANT_project_data (),
     292              :                                             GNUNET_OS_IPK_DATADIR);
     293            0 :     if (NULL == path)
     294              :     {
     295            0 :       GNUNET_break (0);
     296            0 :       return GNUNET_SYSERR;
     297              :     }
     298            0 :     GNUNET_asprintf (&dn,
     299              :                      "%smerchant/static/",
     300              :                      path);
     301            0 :     GNUNET_free (path);
     302              :   }
     303            0 :   ret = GNUNET_DISK_directory_scan (dn,
     304              :                                     &load_static_file,
     305              :                                     NULL);
     306            0 :   if (-1 == ret)
     307              :   {
     308            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     309              :                 "Could not load static resources from `%s': %s\n",
     310              :                 dn,
     311              :                 strerror (errno));
     312            0 :     GNUNET_free (dn);
     313            0 :     return GNUNET_SYSERR;
     314              :   }
     315            0 :   GNUNET_free (dn);
     316            0 :   return GNUNET_OK;
     317              : }
     318              : 
     319              : 
     320              : /**
     321              :  * Nicely shut down.
     322              :  */
     323              : void __attribute__ ((destructor))
     324              : get_statics_fini (void);
     325              : 
     326              : /* Declaration avoids compiler warning */
     327              : void __attribute__ ((destructor))
     328           29 : get_statics_fini ()
     329              : {
     330           29 :   for (unsigned int i = 0; i<loaded_length; i++)
     331              :   {
     332            0 :     GNUNET_free (loaded[i].name);
     333            0 :     GNUNET_free (loaded[i].lang);
     334            0 :     MHD_destroy_response (loaded[i].reply);
     335              :   }
     336           29 :   GNUNET_array_grow (loaded,
     337              :                      loaded_length,
     338              :                      0);
     339           29 : }
        

Generated by: LCOV version 2.0-1