LCOV - code coverage report
Current view: top level - mhd - mhd_legal.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 19.4 % 216 42
Test Date: 2025-12-28 14:06:02 Functions: 40.0 % 5 2

            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 "taler/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              : MHD_RESULT
     125            8 : TALER_MHD_reply_legal (struct MHD_Connection *conn,
     126              :                        struct TALER_MHD_Legal *legal)
     127              : {
     128              :   /* Default terms of service if none are configured */
     129              :   static struct Terms none = {
     130              :     .mime_type = "text/plain",
     131              :     .terms = (void *) "not configured",
     132              :     .language = (void *) "en",
     133              :     .terms_size = strlen ("not configured")
     134              :   };
     135              :   struct MHD_Response *resp;
     136              :   struct Terms *t;
     137              :   struct GNUNET_TIME_Absolute a;
     138              :   struct GNUNET_TIME_Timestamp m;
     139              :   char dat[128];
     140              :   char *langs;
     141              : 
     142            8 :   t = NULL;
     143            8 :   langs = NULL;
     144              : 
     145            8 :   a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING);
     146            8 :   m = GNUNET_TIME_absolute_to_timestamp (a);
     147              :   /* Round up to next full day to ensure the expiration
     148              :      time does not become a fingerprint! */
     149            8 :   a = GNUNET_TIME_absolute_round_down (a,
     150              :                                        MAX_TERMS_CACHING);
     151            8 :   a = GNUNET_TIME_absolute_add (a,
     152              :                                 MAX_TERMS_CACHING);
     153            8 :   TALER_MHD_get_date_string (m.abs_time,
     154              :                              dat);
     155            8 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     156              :               "Setting '%s' header to '%s'\n",
     157              :               MHD_HTTP_HEADER_EXPIRES,
     158              :               dat);
     159            8 :   if (NULL == legal)
     160              :   {
     161            8 :     t = &none;
     162            8 :     goto return_t;
     163              :   }
     164              : 
     165            0 :   if (NULL != legal)
     166              :   {
     167              :     const char *mime;
     168              :     const char *lang;
     169            0 :     double best_mime_q = 0.0;
     170            0 :     double best_lang_q = 0.0;
     171              : 
     172            0 :     mime = MHD_lookup_connection_value (conn,
     173              :                                         MHD_HEADER_KIND,
     174              :                                         MHD_HTTP_HEADER_ACCEPT);
     175            0 :     if (NULL == mime)
     176            0 :       mime = "text/plain";
     177            0 :     lang = MHD_lookup_connection_value (conn,
     178              :                                         MHD_HEADER_KIND,
     179              :                                         MHD_HTTP_HEADER_ACCEPT_LANGUAGE);
     180            0 :     if (NULL == lang)
     181            0 :       lang = "en";
     182              :     /* Find best match: must match mime type (if possible), and if
     183              :        mime type matches, ideally also language */
     184            0 :     for (struct Terms *p = legal->terms_head;
     185            0 :          NULL != p;
     186            0 :          p = p->next)
     187              :     {
     188              :       double q;
     189              : 
     190            0 :       q = TALER_pattern_matches (mime,
     191              :                                  p->mime_type);
     192            0 :       if (q > best_mime_q)
     193            0 :         best_mime_q = q;
     194              :     }
     195            0 :     for (struct Terms *p = legal->terms_head;
     196            0 :          NULL != p;
     197            0 :          p = p->next)
     198              :     {
     199              :       double q;
     200              : 
     201            0 :       q = TALER_pattern_matches (mime,
     202              :                                  p->mime_type);
     203            0 :       if (q < best_mime_q)
     204            0 :         continue;
     205            0 :       q = TALER_pattern_matches (lang,
     206            0 :                                  p->language);
     207              :       /* create 'available-languages' (for this mime-type) */
     208            0 :       if (NULL == langs)
     209              :       {
     210            0 :         langs = GNUNET_strdup (p->language);
     211              :       }
     212            0 :       else if (NULL == strstr (langs,
     213            0 :                                p->language))
     214              :       {
     215            0 :         char *tmp = langs;
     216              : 
     217            0 :         GNUNET_asprintf (&langs,
     218              :                          "%s,%s",
     219              :                          tmp,
     220              :                          p->language);
     221            0 :         GNUNET_free (tmp);
     222              :       }
     223            0 :       if (q < best_lang_q)
     224            0 :         continue;
     225            0 :       best_lang_q = q;
     226            0 :       t = p;
     227              :     }
     228            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     229              :                 "Best match for %s/%s: %s / %s\n",
     230              :                 lang,
     231              :                 mime,
     232              :                 (NULL != t) ? t->mime_type : "<none>",
     233              :                 (NULL != t) ? t->language : "<none>");
     234              :   }
     235              : 
     236            0 :   if (NULL != t)
     237              :   {
     238              :     const char *etag;
     239              : 
     240            0 :     etag = MHD_lookup_connection_value (conn,
     241              :                                         MHD_HEADER_KIND,
     242              :                                         MHD_HTTP_HEADER_IF_NONE_MATCH);
     243            0 :     if ( (NULL != etag) &&
     244            0 :          (NULL != t->terms_etag) &&
     245            0 :          (0 == strcasecmp (etag,
     246            0 :                            t->terms_etag)) )
     247              :     {
     248              :       MHD_RESULT ret;
     249              : 
     250            0 :       resp = MHD_create_response_from_buffer (0,
     251              :                                               NULL,
     252              :                                               MHD_RESPMEM_PERSISTENT);
     253            0 :       TALER_MHD_add_global_headers (resp,
     254              :                                     true);
     255            0 :       GNUNET_break (MHD_YES ==
     256              :                     MHD_add_response_header (resp,
     257              :                                              MHD_HTTP_HEADER_EXPIRES,
     258              :                                              dat));
     259            0 :       GNUNET_break (MHD_YES ==
     260              :                     MHD_add_response_header (resp,
     261              :                                              MHD_HTTP_HEADER_ETAG,
     262              :                                              t->terms_etag));
     263            0 :       if (NULL != legal)
     264            0 :         GNUNET_break (MHD_YES ==
     265              :                       MHD_add_response_header (resp,
     266              :                                                TALER_TERMS_VERSION,
     267              :                                                legal->terms_version));
     268            0 :       ret = MHD_queue_response (conn,
     269              :                                 MHD_HTTP_NOT_MODIFIED,
     270              :                                 resp);
     271            0 :       GNUNET_break (MHD_YES == ret);
     272            0 :       MHD_destroy_response (resp);
     273            0 :       return ret;
     274              :     }
     275              :   }
     276              : 
     277            0 :   if (NULL == t)
     278            0 :     t = &none; /* 501 if not configured */
     279              : 
     280            0 : return_t:
     281              :   /* try to compress the response */
     282            8 :   resp = NULL;
     283            8 :   if ( (TALER_MHD_CT_DEFLATE ==
     284            8 :         TALER_MHD_can_compress (conn,
     285            0 :                                 TALER_MHD_CT_DEFLATE)) &&
     286            0 :        (NULL != t->compressed_terms) )
     287              :   {
     288            0 :     resp = MHD_create_response_from_buffer (t->compressed_terms_size,
     289              :                                             t->compressed_terms,
     290              :                                             MHD_RESPMEM_PERSISTENT);
     291            0 :     if (MHD_NO ==
     292            0 :         MHD_add_response_header (resp,
     293              :                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
     294              :                                  "deflate"))
     295              :     {
     296            0 :       GNUNET_break (0);
     297            0 :       MHD_destroy_response (resp);
     298            0 :       resp = NULL;
     299              :     }
     300              :   }
     301            8 :   if (NULL == resp)
     302              :   {
     303              :     /* could not generate compressed response, return uncompressed */
     304            8 :     resp = MHD_create_response_from_buffer (t->terms_size,
     305              :                                             (void *) t->terms,
     306              :                                             MHD_RESPMEM_PERSISTENT);
     307              :   }
     308            8 :   TALER_MHD_add_global_headers (resp,
     309              :                                 true);
     310            8 :   GNUNET_break (MHD_YES ==
     311              :                 MHD_add_response_header (resp,
     312              :                                          MHD_HTTP_HEADER_EXPIRES,
     313              :                                          dat));
     314            8 :   if (NULL != langs)
     315              :   {
     316            0 :     GNUNET_break (MHD_YES ==
     317              :                   MHD_add_response_header (resp,
     318              :                                            "Avail-Languages",
     319              :                                            langs));
     320            0 :     GNUNET_free (langs);
     321              :   }
     322              :   /* Set cache control headers: our response varies depending on these headers */
     323            8 :   GNUNET_break (MHD_YES ==
     324              :                 MHD_add_response_header (resp,
     325              :                                          MHD_HTTP_HEADER_VARY,
     326              :                                          MHD_HTTP_HEADER_ACCEPT_LANGUAGE ","
     327              :                                          MHD_HTTP_HEADER_ACCEPT ","
     328              :                                          MHD_HTTP_HEADER_ACCEPT_ENCODING));
     329              :   /* Information is always public, revalidate after 10 days */
     330            8 :   GNUNET_break (MHD_YES ==
     331              :                 MHD_add_response_header (resp,
     332              :                                          MHD_HTTP_HEADER_CACHE_CONTROL,
     333              :                                          "public,max-age=864000"));
     334            8 :   if (NULL != t->terms_etag)
     335            0 :     GNUNET_break (MHD_YES ==
     336              :                   MHD_add_response_header (resp,
     337              :                                            MHD_HTTP_HEADER_ETAG,
     338              :                                            t->terms_etag));
     339            8 :   if (NULL != legal)
     340            0 :     GNUNET_break (MHD_YES ==
     341              :                   MHD_add_response_header (resp,
     342              :                                            TALER_TERMS_VERSION,
     343              :                                            legal->terms_version));
     344            8 :   GNUNET_break (MHD_YES ==
     345              :                 MHD_add_response_header (resp,
     346              :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     347              :                                          t->mime_type));
     348            8 :   GNUNET_break (MHD_YES ==
     349              :                 MHD_add_response_header (resp,
     350              :                                          MHD_HTTP_HEADER_CONTENT_LANGUAGE,
     351              :                                          t->language));
     352              :   {
     353              :     MHD_RESULT ret;
     354              : 
     355            8 :     ret = MHD_queue_response (conn,
     356              :                               t == &none
     357              :                               ? MHD_HTTP_NOT_IMPLEMENTED
     358              :                               : MHD_HTTP_OK,
     359              :                               resp);
     360            8 :     MHD_destroy_response (resp);
     361            8 :     return ret;
     362              :   }
     363              : }
     364              : 
     365              : 
     366              : /**
     367              :  * Load all the terms of service from @a path under language @a lang
     368              :  * from file @a name
     369              :  *
     370              :  * @param[in,out] legal where to write the result
     371              :  * @param path where the terms are found
     372              :  * @param lang which language directory to crawl
     373              :  * @param name specific file to access
     374              :  */
     375              : static void
     376            0 : load_terms (struct TALER_MHD_Legal *legal,
     377              :             const char *path,
     378              :             const char *lang,
     379              :             const char *name)
     380              : {
     381              :   static struct MimeMap
     382              :   {
     383              :     const char *ext;
     384              :     const char *mime;
     385              :     unsigned int priority;
     386              :   } mm[] = {
     387              :     { .ext = ".txt", .mime = "text/plain", .priority = 150 },
     388              :     { .ext = ".html", .mime = "text/html", .priority = 100 },
     389              :     { .ext = ".htm", .mime = "text/html", .priority = 99 },
     390              :     { .ext = ".md", .mime = "text/markdown", .priority = 50 },
     391              :     { .ext = ".pdf", .mime = "application/pdf", .priority = 25 },
     392              :     { .ext = ".jpg", .mime = "image/jpeg" },
     393              :     { .ext = ".jpeg", .mime = "image/jpeg" },
     394              :     { .ext = ".png", .mime = "image/png" },
     395              :     { .ext = ".gif", .mime = "image/gif" },
     396              :     { .ext = ".epub", .mime = "application/epub+zip", .priority = 10 },
     397              :     { .ext = ".xml", .mime = "text/xml", .priority = 10 },
     398              :     { .ext = NULL, .mime = NULL }
     399              :   };
     400            0 :   const char *ext = strrchr (name, '.');
     401              :   const char *mime;
     402              :   unsigned int priority;
     403              : 
     404            0 :   if (NULL == ext)
     405              :   {
     406            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     407              :                 "Unsupported file `%s' in directory `%s/%s': lacks extension\n",
     408              :                 name,
     409              :                 path,
     410              :                 lang);
     411            0 :     return;
     412              :   }
     413            0 :   if ( (NULL == legal->terms_version) ||
     414            0 :        (0 != strncmp (legal->terms_version,
     415              :                       name,
     416            0 :                       ext - name - 1)) )
     417              :   {
     418            0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     419              :                 "Filename `%s' does not match Etag `%s' in directory `%s/%s'. Ignoring it.\n",
     420              :                 name,
     421              :                 legal->terms_version,
     422              :                 path,
     423              :                 lang);
     424            0 :     return;
     425              :   }
     426            0 :   mime = NULL;
     427            0 :   for (unsigned int i = 0; NULL != mm[i].ext; i++)
     428            0 :     if (0 == strcasecmp (mm[i].ext,
     429              :                          ext))
     430              :     {
     431            0 :       mime = mm[i].mime;
     432            0 :       priority = mm[i].priority;
     433            0 :       break;
     434              :     }
     435            0 :   if (NULL == mime)
     436              :   {
     437            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     438              :                 "Unsupported file extension `%s' of file `%s' in directory `%s/%s'\n",
     439              :                 ext,
     440              :                 name,
     441              :                 path,
     442              :                 lang);
     443            0 :     return;
     444              :   }
     445              :   /* try to read the file with the terms of service */
     446              :   {
     447              :     struct stat st;
     448              :     char *fn;
     449              :     int fd;
     450              : 
     451            0 :     GNUNET_asprintf (&fn,
     452              :                      "%s/%s/%s",
     453              :                      path,
     454              :                      lang,
     455              :                      name);
     456            0 :     fd = open (fn, O_RDONLY);
     457            0 :     if (-1 == fd)
     458              :     {
     459            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     460              :                                 "open",
     461              :                                 fn);
     462            0 :       GNUNET_free (fn);
     463            0 :       return;
     464              :     }
     465            0 :     if (0 != fstat (fd, &st))
     466              :     {
     467            0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     468              :                                 "fstat",
     469              :                                 fn);
     470            0 :       GNUNET_break (0 == close (fd));
     471            0 :       GNUNET_free (fn);
     472            0 :       return;
     473              :     }
     474              :     if (SIZE_MAX < ((unsigned long long) st.st_size))
     475              :     {
     476              :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     477              :                                 "fstat-size",
     478              :                                 fn);
     479              :       GNUNET_break (0 == close (fd));
     480              :       GNUNET_free (fn);
     481              :       return;
     482              :     }
     483            0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     484              :                 "Loading legal information from file `%s'\n",
     485              :                 fn);
     486              :     {
     487              :       void *buf;
     488              :       size_t bsize;
     489              : 
     490            0 :       bsize = (size_t) st.st_size;
     491            0 :       buf = mmap (NULL,
     492              :                   bsize,
     493              :                   PROT_READ,
     494              :                   MAP_SHARED,
     495              :                   fd,
     496              :                   0);
     497            0 :       if (MAP_FAILED == buf)
     498              :       {
     499            0 :         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     500              :                                   "mmap",
     501              :                                   fn);
     502            0 :         GNUNET_break (0 == close (fd));
     503            0 :         GNUNET_free (fn);
     504            0 :         return;
     505              :       }
     506            0 :       GNUNET_break (0 == close (fd));
     507            0 :       GNUNET_free (fn);
     508              : 
     509              :       /* insert into global list of terms of service */
     510              :       {
     511              :         struct Terms *t;
     512              :         struct GNUNET_HashCode hc;
     513              : 
     514            0 :         GNUNET_CRYPTO_hash (buf,
     515              :                             bsize,
     516              :                             &hc);
     517            0 :         t = GNUNET_new (struct Terms);
     518            0 :         t->mime_type = mime;
     519            0 :         t->terms = buf;
     520            0 :         t->language = GNUNET_strdup (lang);
     521            0 :         t->terms_size = bsize;
     522            0 :         t->priority = priority;
     523              :         t->terms_etag
     524            0 :           = GNUNET_STRINGS_data_to_string_alloc (&hc,
     525              :                                                  sizeof (hc) / 2);
     526            0 :         buf = GNUNET_memdup (t->terms,
     527              :                              t->terms_size);
     528            0 :         if (TALER_MHD_body_compress (&buf,
     529              :                                      &bsize))
     530              :         {
     531            0 :           t->compressed_terms = buf;
     532            0 :           t->compressed_terms_size = bsize;
     533              :         }
     534              :         else
     535              :         {
     536            0 :           GNUNET_free (buf);
     537              :         }
     538              :         {
     539            0 :           struct Terms *prev = NULL;
     540              : 
     541            0 :           for (struct Terms *pos = legal->terms_head;
     542            0 :                NULL != pos;
     543            0 :                pos = pos->next)
     544              :           {
     545            0 :             if (pos->priority < priority)
     546            0 :               break;
     547            0 :             prev = pos;
     548              :           }
     549            0 :           GNUNET_CONTAINER_DLL_insert_after (legal->terms_head,
     550              :                                              legal->terms_tail,
     551              :                                              prev,
     552              :                                              t);
     553              :         }
     554              :       }
     555              :     }
     556              :   }
     557              : }
     558              : 
     559              : 
     560              : /**
     561              :  * Load all the terms of service from @a path under language @a lang.
     562              :  *
     563              :  * @param[in,out] legal where to write the result
     564              :  * @param path where the terms are found
     565              :  * @param lang which language directory to crawl
     566              :  */
     567              : static void
     568            0 : load_language (struct TALER_MHD_Legal *legal,
     569              :                const char *path,
     570              :                const char *lang)
     571              : {
     572              :   char *dname;
     573              :   DIR *d;
     574              : 
     575            0 :   GNUNET_asprintf (&dname,
     576              :                    "%s/%s",
     577              :                    path,
     578              :                    lang);
     579            0 :   d = opendir (dname);
     580            0 :   if (NULL == d)
     581              :   {
     582            0 :     GNUNET_free (dname);
     583            0 :     return;
     584              :   }
     585            0 :   for (struct dirent *de = readdir (d);
     586            0 :        NULL != de;
     587            0 :        de = readdir (d))
     588              :   {
     589            0 :     const char *fn = de->d_name;
     590              : 
     591            0 :     if (fn[0] == '.')
     592            0 :       continue;
     593            0 :     load_terms (legal,
     594              :                 path,
     595              :                 lang,
     596              :                 fn);
     597              :   }
     598            0 :   GNUNET_break (0 == closedir (d));
     599            0 :   GNUNET_free (dname);
     600              : }
     601              : 
     602              : 
     603              : struct TALER_MHD_Legal *
     604           36 : TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
     605              :                       const char *section,
     606              :                       const char *diroption,
     607              :                       const char *tagoption)
     608              : {
     609              :   struct TALER_MHD_Legal *legal;
     610              :   char *path;
     611              :   DIR *d;
     612              : 
     613           36 :   legal = GNUNET_new (struct TALER_MHD_Legal);
     614           36 :   if (GNUNET_OK !=
     615           36 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     616              :                                              section,
     617              :                                              tagoption,
     618              :                                              &legal->terms_version))
     619              :   {
     620            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     621              :                                section,
     622              :                                tagoption);
     623            0 :     GNUNET_free (legal);
     624            0 :     return NULL;
     625              :   }
     626           36 :   if (GNUNET_OK !=
     627           36 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     628              :                                                section,
     629              :                                                diroption,
     630              :                                                &path))
     631              :   {
     632            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     633              :                                section,
     634              :                                diroption);
     635            0 :     GNUNET_free (legal->terms_version);
     636            0 :     GNUNET_free (legal);
     637            0 :     return NULL;
     638              :   }
     639           36 :   d = opendir (path);
     640           36 :   if (NULL == d)
     641              :   {
     642           36 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
     643              :                                section,
     644              :                                diroption,
     645              :                                "Could not open directory");
     646           36 :     GNUNET_free (legal->terms_version);
     647           36 :     GNUNET_free (legal);
     648           36 :     GNUNET_free (path);
     649           36 :     return NULL;
     650              :   }
     651            0 :   for (struct dirent *de = readdir (d);
     652            0 :        NULL != de;
     653            0 :        de = readdir (d))
     654              :   {
     655            0 :     const char *lang = de->d_name;
     656              : 
     657            0 :     if (lang[0] == '.')
     658            0 :       continue;
     659            0 :     if (0 == strcmp (lang,
     660              :                      "locale"))
     661            0 :       continue;
     662            0 :     load_language (legal,
     663              :                    path,
     664              :                    lang);
     665              :   }
     666            0 :   GNUNET_break (0 == closedir (d));
     667            0 :   GNUNET_free (path);
     668            0 :   return legal;
     669              : }
     670              : 
     671              : 
     672              : void
     673            0 : TALER_MHD_legal_free (struct TALER_MHD_Legal *legal)
     674              : {
     675              :   struct Terms *t;
     676            0 :   if (NULL == legal)
     677            0 :     return;
     678            0 :   while (NULL != (t = legal->terms_head))
     679              :   {
     680            0 :     GNUNET_free (t->language);
     681            0 :     GNUNET_free (t->compressed_terms);
     682            0 :     if (0 != munmap (t->terms, t->terms_size))
     683            0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     684              :                            "munmap");
     685            0 :     GNUNET_CONTAINER_DLL_remove (legal->terms_head,
     686              :                                  legal->terms_tail,
     687              :                                  t);
     688            0 :     GNUNET_free (t->terms_etag);
     689            0 :     GNUNET_free (t);
     690              :   }
     691            0 :   GNUNET_free (legal->terms_version);
     692            0 :   GNUNET_free (legal);
     693              : }
        

Generated by: LCOV version 2.0-1