LCOV - code coverage report
Current view: top level - mhd - mhd_legal.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 77.0 % 235 181
Test Date: 2026-10-17 15:46:49 Functions: 100.0 % 6 6

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2019, 2020, 2022, 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 Affero 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 Affero General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU Affero General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file mhd_legal.c
      18              :  * @brief API for returning legal documents based on client language
      19              :  *        and content type preferences
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <gnunet/gnunet_util_lib.h>
      24              : #include <gnunet/gnunet_json_lib.h>
      25              : #include <jansson.h>
      26              : #include <microhttpd.h>
      27              : #include "taler/taler_util.h"
      28              : #include "taler/taler_mhd_lib.h"
      29              : 
      30              : /**
      31              :  * How long should browsers/proxies cache the "legal" replies?
      32              :  */
      33              : #define MAX_TERMS_CACHING GNUNET_TIME_UNIT_DAYS
      34              : 
      35              : /**
      36              :  * HTTP header with the version of the terms of service.
      37              :  */
      38              : #define TALER_TERMS_VERSION "Taler-Terms-Version"
      39              : 
      40              : /**
      41              :  * Entry in the terms-of-service array.
      42              :  */
      43              : struct Terms
      44              : {
      45              :   /**
      46              :    * Kept in a DLL.
      47              :    */
      48              :   struct Terms *prev;
      49              : 
      50              :   /**
      51              :    * Kept in a DLL.
      52              :    */
      53              :   struct Terms *next;
      54              : 
      55              :   /**
      56              :    * Mime type of the terms.
      57              :    */
      58              :   const char *mime_type;
      59              : 
      60              :   /**
      61              :    * The terms (NOT 0-terminated!), mmap()'ed. Do not free,
      62              :    * use munmap() instead.
      63              :    */
      64              :   void *terms;
      65              : 
      66              :   /**
      67              :    * The desired language.
      68              :    */
      69              :   char *language;
      70              : 
      71              :   /**
      72              :    * deflated @e terms, to return if client supports deflate compression.
      73              :    * malloc()'ed.  NULL if @e terms does not compress.
      74              :    */
      75              :   void *compressed_terms;
      76              : 
      77              :   /**
      78              :    * Etag we use for this response.
      79              :    */
      80              :   char *terms_etag;
      81              : 
      82              :   /**
      83              :    * Number of bytes in @e terms.
      84              :    */
      85              :   size_t terms_size;
      86              : 
      87              :   /**
      88              :    * Number of bytes in @e compressed_terms.
      89              :    */
      90              :   size_t compressed_terms_size;
      91              : 
      92              :   /**
      93              :    * Sorting key by format preference in case
      94              :    * everything else is equal. Higher is preferred.
      95              :    */
      96              :   unsigned int priority;
      97              : 
      98              : };
      99              : 
     100              : 
     101              : /**
     102              :  * Prepared responses for legal documents
     103              :  * (terms of service, privacy policy).
     104              :  */
     105              : struct TALER_MHD_Legal
     106              : {
     107              :   /**
     108              :    * DLL of terms of service.
     109              :    */
     110              :   struct Terms *terms_head;
     111              : 
     112              :   /**
     113              :    * DLL of terms of service.
     114              :    */
     115              :   struct Terms *terms_tail;
     116              : 
     117              :   /**
     118              :    * Etag to use for the terms of service (= version).
     119              :    */
     120              :   char *terms_version;
     121              : };
     122              : 
     123              : 
     124              : /**
     125              :  * Check if the comma-separated list @a langs already contains
     126              :  * the entry @a lang.
     127              :  *
     128              :  * @param langs comma-separated list of languages
     129              :  * @param lang language to look for
     130              :  * @return true if @a lang is an entry of @a langs
     131              :  */
     132              : static bool
     133           17 : lang_in_list (const char *langs,
     134              :               const char *lang)
     135              : {
     136           17 :   size_t len = strlen (lang);
     137           17 :   const char *pos = langs;
     138              : 
     139           26 :   while (NULL != pos)
     140              :   {
     141           19 :     const char *end = strchr (pos,
     142              :                               ',');
     143           19 :     size_t elen = (NULL == end)
     144            9 :                   ? strlen (pos)
     145           19 :                   : (size_t) (end - pos);
     146              : 
     147           19 :     if ( (elen == len) &&
     148           19 :          (0 == strncasecmp (pos,
     149              :                             lang,
     150              :                             len)) )
     151           10 :       return true;
     152            9 :     pos = (NULL == end)
     153              :           ? NULL
     154            9 :           : end + 1;
     155              :   }
     156            7 :   return false;
     157              : }
     158              : 
     159              : 
     160              : enum MHD_Result
     161           19 : TALER_MHD_reply_legal (struct MHD_Connection *conn,
     162              :                        struct TALER_MHD_Legal *legal)
     163              : {
     164              :   /* Default terms of service if none are configured */
     165              :   static struct Terms none = {
     166              :     .mime_type = "text/plain",
     167              :     .terms = (void *) "not configured",
     168              :     .language = (void *) "en",
     169              :     .terms_size = strlen ("not configured")
     170              :   };
     171              :   struct MHD_Response *resp;
     172              :   struct Terms *t;
     173              :   struct GNUNET_TIME_Absolute a;
     174              :   struct GNUNET_TIME_Timestamp m;
     175              :   char dat[128];
     176              :   char *langs;
     177              : 
     178           19 :   t = NULL;
     179           19 :   langs = NULL;
     180              : 
     181           19 :   a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING);
     182              :   /* Round up to next full day to ensure the expiration
     183              :      time does not become a fingerprint! */
     184           19 :   a = GNUNET_TIME_absolute_round_down (a,
     185              :                                        MAX_TERMS_CACHING);
     186           19 :   a = GNUNET_TIME_absolute_add (a,
     187              :                                 MAX_TERMS_CACHING);
     188           19 :   m = GNUNET_TIME_absolute_to_timestamp (a);
     189           19 :   TALER_MHD_get_date_string (m.abs_time,
     190              :                              dat);
     191           19 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     192              :               "Setting '%s' header to '%s'\n",
     193              :               MHD_HTTP_HEADER_EXPIRES,
     194              :               dat);
     195           19 :   if (NULL == legal)
     196              :   {
     197            8 :     t = &none;
     198            8 :     goto return_t;
     199              :   }
     200              : 
     201           11 :   if (NULL != legal)
     202              :   {
     203              :     const char *mime;
     204              :     const char *lang;
     205           11 :     double best_mime_q = 0.0;
     206           11 :     double best_lang_q = 0.0;
     207              : 
     208           11 :     mime = MHD_lookup_connection_value (conn,
     209              :                                         MHD_HEADER_KIND,
     210              :                                         MHD_HTTP_HEADER_ACCEPT);
     211           11 :     if (NULL == mime)
     212            1 :       mime = "text/plain";
     213           11 :     lang = MHD_lookup_connection_value (conn,
     214              :                                         MHD_HEADER_KIND,
     215              :                                         MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
     216           11 :     if (NULL == lang)
     217            9 :       lang = "en";
     218              :     /* Find best match: must match mime type (if possible), and if
     219              :        mime type matches, ideally also language */
     220           11 :     for (struct Terms *p = legal->terms_head;
     221           77 :          NULL != p;
     222           66 :          p = p->next)
     223              :     {
     224              :       double q;
     225              : 
     226           66 :       q = TALER_pattern_matches (mime,
     227              :                                  p->mime_type);
     228           66 :       if (q > best_mime_q)
     229           13 :         best_mime_q = q;
     230              :     }
     231           11 :     for (struct Terms *p = legal->terms_head;
     232           77 :          NULL != p;
     233           66 :          p = p->next)
     234              :     {
     235              :       double q;
     236              : 
     237           66 :       q = TALER_pattern_matches (mime,
     238              :                                  p->mime_type);
     239           66 :       if (q < best_mime_q)
     240           38 :         continue;
     241           28 :       q = TALER_pattern_matches (lang,
     242           28 :                                  p->language);
     243              :       /* create 'available-languages' (for this mime-type) */
     244           28 :       if (NULL == langs)
     245              :       {
     246           11 :         langs = GNUNET_strdup (p->language);
     247              :       }
     248           17 :       else if (! lang_in_list (langs,
     249           17 :                                p->language))
     250              :       {
     251            7 :         char *tmp = langs;
     252              : 
     253            7 :         GNUNET_asprintf (&langs,
     254              :                          "%s,%s",
     255              :                          tmp,
     256              :                          p->language);
     257            7 :         GNUNET_free (tmp);
     258              :       }
     259           28 :       if (NULL != t)
     260              :       {
     261           17 :         if (q < best_lang_q)
     262            9 :           continue;
     263              :         /* Client expressed no preference between these two formats,
     264              :            so fall back to our own ranking instead of letting the
     265              :            last entry in the list win. */
     266            8 :         if ( (q <= best_lang_q) &&
     267            6 :              (p->priority <= t->priority) )
     268            6 :           continue;
     269              :       }
     270           13 :       best_lang_q = q;
     271           13 :       t = p;
     272              :     }
     273           11 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     274              :                 "Best match for %s/%s: %s / %s\n",
     275              :                 lang,
     276              :                 mime,
     277              :                 (NULL != t) ? t->mime_type : "<none>",
     278              :                 (NULL != t) ? t->language : "<none>");
     279              :   }
     280              : 
     281           11 :   if (NULL != t)
     282              :   {
     283              :     const char *etag;
     284              : 
     285           11 :     etag = MHD_lookup_connection_value (conn,
     286              :                                         MHD_HEADER_KIND,
     287              :                                         MHD_HTTP_HEADER_IF_NONE_MATCH);
     288           11 :     if ( (NULL != etag) &&
     289            0 :          (NULL != t->terms_etag) &&
     290            0 :          (0 == strcasecmp (etag,
     291            0 :                            t->terms_etag)) )
     292              :     {
     293              :       enum MHD_Result ret;
     294              : 
     295            0 :       resp = MHD_create_response_from_buffer (0,
     296              :                                               NULL,
     297              :                                               MHD_RESPMEM_PERSISTENT);
     298            0 :       TALER_MHD_add_global_headers (resp,
     299              :                                     true);
     300            0 :       GNUNET_break (MHD_YES ==
     301              :                     MHD_add_response_header (resp,
     302              :                                              MHD_HTTP_HEADER_EXPIRES,
     303              :                                              dat));
     304            0 :       GNUNET_break (MHD_YES ==
     305              :                     MHD_add_response_header (resp,
     306              :                                              MHD_HTTP_HEADER_ETAG,
     307              :                                              t->terms_etag));
     308            0 :       if (NULL != legal)
     309            0 :         GNUNET_break (MHD_YES ==
     310              :                       MHD_add_response_header (resp,
     311              :                                                TALER_TERMS_VERSION,
     312              :                                                legal->terms_version));
     313            0 :       ret = MHD_queue_response (conn,
     314              :                                 MHD_HTTP_NOT_MODIFIED,
     315              :                                 resp);
     316            0 :       GNUNET_break (MHD_YES == ret);
     317            0 :       MHD_destroy_response (resp);
     318            0 :       GNUNET_free (langs);
     319            0 :       return ret;
     320              :     }
     321              :   }
     322              : 
     323           11 :   if (NULL == t)
     324            0 :     t = &none; /* 501 if not configured */
     325              : 
     326           11 : return_t:
     327              :   /* try to compress the response */
     328           19 :   resp = NULL;
     329           19 :   if ( (TALER_MHD_CT_DEFLATE ==
     330           19 :         TALER_MHD_can_compress (conn,
     331            0 :                                 TALER_MHD_CT_DEFLATE)) &&
     332            0 :        (NULL != t->compressed_terms) )
     333              :   {
     334            0 :     resp = MHD_create_response_from_buffer (t->compressed_terms_size,
     335              :                                             t->compressed_terms,
     336              :                                             MHD_RESPMEM_PERSISTENT);
     337            0 :     if (MHD_NO ==
     338            0 :         MHD_add_response_header (resp,
     339              :                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
     340              :                                  "deflate"))
     341              :     {
     342            0 :       GNUNET_break (0);
     343            0 :       MHD_destroy_response (resp);
     344            0 :       resp = NULL;
     345              :     }
     346              :   }
     347           19 :   if (NULL == resp)
     348              :   {
     349              :     /* could not generate compressed response, return uncompressed */
     350           19 :     resp = MHD_create_response_from_buffer (t->terms_size,
     351              :                                             (void *) t->terms,
     352              :                                             MHD_RESPMEM_PERSISTENT);
     353              :   }
     354           19 :   TALER_MHD_add_global_headers (resp,
     355              :                                 true);
     356           19 :   GNUNET_break (MHD_YES ==
     357              :                 MHD_add_response_header (resp,
     358              :                                          MHD_HTTP_HEADER_EXPIRES,
     359              :                                          dat));
     360           19 :   if (NULL != langs)
     361              :   {
     362           11 :     GNUNET_break (MHD_YES ==
     363              :                   MHD_add_response_header (resp,
     364              :                                            "Avail-Languages",
     365              :                                            langs));
     366           11 :     GNUNET_free (langs);
     367              :   }
     368              :   /* Set cache control headers: our response varies depending on these headers */
     369           19 :   GNUNET_break (MHD_YES ==
     370              :                 MHD_add_response_header (resp,
     371              :                                          MHD_HTTP_HEADER_VARY,
     372              :                                          MHD_HTTP_HEADER_ACCEPT_LANGUAGE ","
     373              :                                          MHD_HTTP_HEADER_ACCEPT ","
     374              :                                          MHD_HTTP_HEADER_ACCEPT_ENCODING));
     375              :   /* Information is always public, revalidate after 10 days */
     376           19 :   GNUNET_break (MHD_YES ==
     377              :                 MHD_add_response_header (resp,
     378              :                                          MHD_HTTP_HEADER_CACHE_CONTROL,
     379              :                                          "public,max-age=864000"));
     380           19 :   if (NULL != t->terms_etag)
     381           11 :     GNUNET_break (MHD_YES ==
     382              :                   MHD_add_response_header (resp,
     383              :                                            MHD_HTTP_HEADER_ETAG,
     384              :                                            t->terms_etag));
     385           19 :   if (NULL != legal)
     386           11 :     GNUNET_break (MHD_YES ==
     387              :                   MHD_add_response_header (resp,
     388              :                                            TALER_TERMS_VERSION,
     389              :                                            legal->terms_version));
     390           19 :   GNUNET_break (MHD_YES ==
     391              :                 MHD_add_response_header (resp,
     392              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     393              :                                          t->mime_type));
     394           19 :   GNUNET_break (MHD_YES ==
     395              :                 MHD_add_response_header (resp,
     396              :                                          MHD_HTTP_HEADER_CONTENT_LANGUAGE,
     397              :                                          t->language));
     398              :   {
     399              :     enum MHD_Result ret;
     400              : 
     401           19 :     ret = MHD_queue_response (conn,
     402              :                               t == &none
     403              :                               ? MHD_HTTP_NOT_IMPLEMENTED
     404              :                               : MHD_HTTP_OK,
     405              :                               resp);
     406           19 :     MHD_destroy_response (resp);
     407           19 :     return ret;
     408              :   }
     409              : }
     410              : 
     411              : 
     412              : /**
     413              :  * Load all the terms of service from @a path under language @a lang
     414              :  * from file @a name
     415              :  *
     416              :  * @param[in,out] legal where to write the result
     417              :  * @param path where the terms are found
     418              :  * @param lang which language directory to crawl
     419              :  * @param name specific file to access
     420              :  */
     421              : static void
     422            6 : load_terms (struct TALER_MHD_Legal *legal,
     423              :             const char *path,
     424              :             const char *lang,
     425              :             const char *name)
     426              : {
     427              :   static struct MimeMap
     428              :   {
     429              :     const char *ext;
     430              :     const char *mime;
     431              :     unsigned int priority;
     432              :   } mm[] = {
     433              :     { .ext = ".txt", .mime = "text/plain", .priority = 150 },
     434              :     { .ext = ".html", .mime = "text/html", .priority = 100 },
     435              :     { .ext = ".htm", .mime = "text/html", .priority = 99 },
     436              :     { .ext = ".md", .mime = "text/markdown", .priority = 50 },
     437              :     { .ext = ".pdf", .mime = "application/pdf", .priority = 25 },
     438              :     { .ext = ".jpg", .mime = "image/jpeg" },
     439              :     { .ext = ".jpeg", .mime = "image/jpeg" },
     440              :     { .ext = ".png", .mime = "image/png" },
     441              :     { .ext = ".gif", .mime = "image/gif" },
     442              :     { .ext = ".epub", .mime = "application/epub+zip", .priority = 10 },
     443              :     { .ext = ".xml", .mime = "text/xml", .priority = 10 },
     444              :     { .ext = NULL, .mime = NULL }
     445              :   };
     446            6 :   const char *ext = strrchr (name, '.');
     447              :   const char *mime;
     448              :   unsigned int priority;
     449              : 
     450            6 :   if (NULL == ext)
     451              :   {
     452            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     453              :                 "Unsupported file `%s' in directory `%s/%s': lacks extension\n",
     454              :                 name,
     455              :                 path,
     456              :                 lang);
     457            0 :     return;
     458              :   }
     459            6 :   if ( (NULL == legal->terms_version) ||
     460            6 :        (0 != strncmp (legal->terms_version,
     461              :                       name,
     462            6 :                       ext - name)) )
     463              :   {
     464            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     465              :                 "Filename `%s' does not match Etag `%s' in directory `%s/%s'. Ignoring it.\n",
     466              :                 name,
     467              :                 legal->terms_version,
     468              :                 path,
     469              :                 lang);
     470            0 :     return;
     471              :   }
     472            6 :   mime = NULL;
     473           18 :   for (unsigned int i = 0; NULL != mm[i].ext; i++)
     474           18 :     if (0 == strcasecmp (mm[i].ext,
     475              :                          ext))
     476              :     {
     477            6 :       mime = mm[i].mime;
     478            6 :       priority = mm[i].priority;
     479            6 :       break;
     480              :     }
     481            6 :   if (NULL == mime)
     482              :   {
     483            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     484              :                 "Unsupported file extension `%s' of file `%s' in directory `%s/%s'\n",
     485              :                 ext,
     486              :                 name,
     487              :                 path,
     488              :                 lang);
     489            0 :     return;
     490              :   }
     491              :   /* try to read the file with the terms of service */
     492              :   {
     493              :     struct stat st;
     494              :     char *fn;
     495              :     int fd;
     496              : 
     497            6 :     GNUNET_asprintf (&fn,
     498              :                      "%s/%s/%s",
     499              :                      path,
     500              :                      lang,
     501              :                      name);
     502            6 :     fd = open (fn, O_RDONLY);
     503            6 :     if (-1 == fd)
     504              :     {
     505            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     506              :                                 "open",
     507              :                                 fn);
     508            0 :       GNUNET_free (fn);
     509            0 :       return;
     510              :     }
     511            6 :     if (0 != fstat (fd, &st))
     512              :     {
     513            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     514              :                                 "fstat",
     515              :                                 fn);
     516            0 :       GNUNET_break (0 == close (fd));
     517            0 :       GNUNET_free (fn);
     518            0 :       return;
     519              :     }
     520              :     if (SIZE_MAX < ((unsigned long long) st.st_size))
     521              :     {
     522              :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     523              :                                 "fstat-size",
     524              :                                 fn);
     525              :       GNUNET_break (0 == close (fd));
     526              :       GNUNET_free (fn);
     527              :       return;
     528              :     }
     529            6 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     530              :                 "Loading legal information from file `%s'\n",
     531              :                 fn);
     532              :     {
     533              :       void *buf;
     534              :       size_t bsize;
     535              : 
     536            6 :       bsize = (size_t) st.st_size;
     537            6 :       buf = mmap (NULL,
     538              :                   bsize,
     539              :                   PROT_READ,
     540              :                   MAP_SHARED,
     541              :                   fd,
     542              :                   0);
     543            6 :       if (MAP_FAILED == buf)
     544              :       {
     545            0 :         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     546              :                                   "mmap",
     547              :                                   fn);
     548            0 :         GNUNET_break (0 == close (fd));
     549            0 :         GNUNET_free (fn);
     550            0 :         return;
     551              :       }
     552            6 :       GNUNET_break (0 == close (fd));
     553            6 :       GNUNET_free (fn);
     554              : 
     555              :       /* insert into global list of terms of service */
     556              :       {
     557              :         struct Terms *t;
     558              :         struct GNUNET_HashCode hc;
     559              : 
     560            6 :         GNUNET_CRYPTO_hash (buf,
     561              :                             bsize,
     562              :                             &hc);
     563            6 :         t = GNUNET_new (struct Terms);
     564            6 :         t->mime_type = mime;
     565            6 :         t->terms = buf;
     566            6 :         t->language = GNUNET_strdup (lang);
     567            6 :         t->terms_size = bsize;
     568            6 :         t->priority = priority;
     569              :         t->terms_etag
     570            6 :           = GNUNET_STRINGS_data_to_string_alloc (&hc,
     571              :                                                  sizeof (hc) / 2);
     572            6 :         buf = GNUNET_memdup (t->terms,
     573              :                              t->terms_size);
     574            6 :         if (TALER_MHD_body_compress (&buf,
     575              :                                      &bsize))
     576              :         {
     577            0 :           t->compressed_terms = buf;
     578            0 :           t->compressed_terms_size = bsize;
     579              :         }
     580              :         else
     581              :         {
     582            6 :           GNUNET_free (buf);
     583              :         }
     584              :         {
     585            6 :           struct Terms *prev = NULL;
     586              : 
     587            6 :           for (struct Terms *pos = legal->terms_head;
     588           15 :                NULL != pos;
     589            9 :                pos = pos->next)
     590              :           {
     591           12 :             if (pos->priority < priority)
     592            3 :               break;
     593            9 :             prev = pos;
     594              :           }
     595            6 :           GNUNET_CONTAINER_DLL_insert_after (legal->terms_head,
     596              :                                              legal->terms_tail,
     597              :                                              prev,
     598              :                                              t);
     599              :         }
     600              :       }
     601              :     }
     602              :   }
     603              : }
     604              : 
     605              : 
     606              : /**
     607              :  * Load all the terms of service from @a path under language @a lang.
     608              :  *
     609              :  * @param[in,out] legal where to write the result
     610              :  * @param path where the terms are found
     611              :  * @param lang which language directory to crawl
     612              :  */
     613              : static void
     614            2 : load_language (struct TALER_MHD_Legal *legal,
     615              :                const char *path,
     616              :                const char *lang)
     617              : {
     618              :   char *dname;
     619              :   DIR *d;
     620              : 
     621            2 :   GNUNET_asprintf (&dname,
     622              :                    "%s/%s",
     623              :                    path,
     624              :                    lang);
     625            2 :   d = opendir (dname);
     626            2 :   if (NULL == d)
     627              :   {
     628            0 :     GNUNET_free (dname);
     629            0 :     return;
     630              :   }
     631            2 :   for (struct dirent *de = readdir (d);
     632           12 :        NULL != de;
     633           10 :        de = readdir (d))
     634              :   {
     635           10 :     const char *fn = de->d_name;
     636              : 
     637           10 :     if (fn[0] == '.')
     638            4 :       continue;
     639            6 :     load_terms (legal,
     640              :                 path,
     641              :                 lang,
     642              :                 fn);
     643              :   }
     644            2 :   GNUNET_break (0 == closedir (d));
     645            2 :   GNUNET_free (dname);
     646              : }
     647              : 
     648              : 
     649              : struct TALER_MHD_Legal *
     650           41 : TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
     651              :                       const char *section,
     652              :                       const char *diroption,
     653              :                       const char *tagoption)
     654              : {
     655              :   struct TALER_MHD_Legal *legal;
     656              :   char *path;
     657              :   DIR *d;
     658              : 
     659           41 :   legal = GNUNET_new (struct TALER_MHD_Legal);
     660           41 :   if (GNUNET_OK !=
     661           41 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     662              :                                              section,
     663              :                                              tagoption,
     664              :                                              &legal->terms_version))
     665              :   {
     666            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     667              :                                section,
     668              :                                tagoption);
     669            0 :     GNUNET_free (legal);
     670            0 :     return NULL;
     671              :   }
     672           41 :   if (GNUNET_OK !=
     673           41 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     674              :                                                section,
     675              :                                                diroption,
     676              :                                                &path))
     677              :   {
     678            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     679              :                                section,
     680              :                                diroption);
     681            0 :     GNUNET_free (legal->terms_version);
     682            0 :     GNUNET_free (legal);
     683            0 :     return NULL;
     684              :   }
     685           41 :   d = opendir (path);
     686           41 :   if (NULL == d)
     687              :   {
     688           40 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
     689              :                                section,
     690              :                                diroption,
     691              :                                "Could not open directory");
     692           40 :     GNUNET_free (legal->terms_version);
     693           40 :     GNUNET_free (legal);
     694           40 :     GNUNET_free (path);
     695           40 :     return NULL;
     696              :   }
     697            1 :   for (struct dirent *de = readdir (d);
     698            5 :        NULL != de;
     699            4 :        de = readdir (d))
     700              :   {
     701            4 :     const char *lang = de->d_name;
     702              : 
     703            4 :     if (lang[0] == '.')
     704            2 :       continue;
     705            2 :     if (0 == strcmp (lang,
     706              :                      "locale"))
     707            0 :       continue;
     708            2 :     load_language (legal,
     709              :                    path,
     710              :                    lang);
     711              :   }
     712            1 :   GNUNET_break (0 == closedir (d));
     713            1 :   GNUNET_free (path);
     714            1 :   return legal;
     715              : }
     716              : 
     717              : 
     718              : void
     719            1 : TALER_MHD_legal_free (struct TALER_MHD_Legal *legal)
     720              : {
     721              :   struct Terms *t;
     722            1 :   if (NULL == legal)
     723            0 :     return;
     724            7 :   while (NULL != (t = legal->terms_head))
     725              :   {
     726            6 :     GNUNET_free (t->language);
     727            6 :     GNUNET_free (t->compressed_terms);
     728            6 :     if (0 != munmap (t->terms, t->terms_size))
     729            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     730              :                            "munmap");
     731            6 :     GNUNET_CONTAINER_DLL_remove (legal->terms_head,
     732              :                                  legal->terms_tail,
     733              :                                  t);
     734            6 :     GNUNET_free (t->terms_etag);
     735            6 :     GNUNET_free (t);
     736              :   }
     737            1 :   GNUNET_free (legal->terms_version);
     738            1 :   GNUNET_free (legal);
     739              : }
        

Generated by: LCOV version 2.0-1