LCOV - code coverage report
Current view: top level - mhd - mhd_legal.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 42 215 19.5 %
Date: 2025-06-22 12:09:43 Functions: 2 5 40.0 %

          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          10 : 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          10 :   t = NULL;
     143          10 :   langs = NULL;
     144             : 
     145          10 :   a = GNUNET_TIME_relative_to_absolute (MAX_TERMS_CACHING);
     146          10 :   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          10 :   a = GNUNET_TIME_absolute_round_down (a,
     150             :                                        MAX_TERMS_CACHING);
     151          10 :   a = GNUNET_TIME_absolute_add (a,
     152             :                                 MAX_TERMS_CACHING);
     153          10 :   TALER_MHD_get_date_string (m.abs_time,
     154             :                              dat);
     155          10 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     156             :               "Setting '%s' header to '%s'\n",
     157             :               MHD_HTTP_HEADER_EXPIRES,
     158             :               dat);
     159          10 :   if (NULL == legal)
     160             :   {
     161          10 :     t = &none;
     162          10 :     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          10 :   resp = NULL;
     283          10 :   if ( (MHD_YES ==
     284          10 :         TALER_MHD_can_compress (conn)) &&
     285           0 :        (NULL != t->compressed_terms) )
     286             :   {
     287           0 :     resp = MHD_create_response_from_buffer (t->compressed_terms_size,
     288             :                                             t->compressed_terms,
     289             :                                             MHD_RESPMEM_PERSISTENT);
     290           0 :     if (MHD_NO ==
     291           0 :         MHD_add_response_header (resp,
     292             :                                  MHD_HTTP_HEADER_CONTENT_ENCODING,
     293             :                                  "deflate"))
     294             :     {
     295           0 :       GNUNET_break (0);
     296           0 :       MHD_destroy_response (resp);
     297           0 :       resp = NULL;
     298             :     }
     299             :   }
     300          10 :   if (NULL == resp)
     301             :   {
     302             :     /* could not generate compressed response, return uncompressed */
     303          10 :     resp = MHD_create_response_from_buffer (t->terms_size,
     304             :                                             (void *) t->terms,
     305             :                                             MHD_RESPMEM_PERSISTENT);
     306             :   }
     307          10 :   TALER_MHD_add_global_headers (resp,
     308             :                                 true);
     309          10 :   GNUNET_break (MHD_YES ==
     310             :                 MHD_add_response_header (resp,
     311             :                                          MHD_HTTP_HEADER_EXPIRES,
     312             :                                          dat));
     313          10 :   if (NULL != langs)
     314             :   {
     315           0 :     GNUNET_break (MHD_YES ==
     316             :                   MHD_add_response_header (resp,
     317             :                                            "Avail-Languages",
     318             :                                            langs));
     319           0 :     GNUNET_free (langs);
     320             :   }
     321             :   /* Set cache control headers: our response varies depending on these headers */
     322          10 :   GNUNET_break (MHD_YES ==
     323             :                 MHD_add_response_header (resp,
     324             :                                          MHD_HTTP_HEADER_VARY,
     325             :                                          MHD_HTTP_HEADER_ACCEPT_LANGUAGE ","
     326             :                                          MHD_HTTP_HEADER_ACCEPT ","
     327             :                                          MHD_HTTP_HEADER_ACCEPT_ENCODING));
     328             :   /* Information is always public, revalidate after 10 days */
     329          10 :   GNUNET_break (MHD_YES ==
     330             :                 MHD_add_response_header (resp,
     331             :                                          MHD_HTTP_HEADER_CACHE_CONTROL,
     332             :                                          "public,max-age=864000"));
     333          10 :   if (NULL != t->terms_etag)
     334           0 :     GNUNET_break (MHD_YES ==
     335             :                   MHD_add_response_header (resp,
     336             :                                            MHD_HTTP_HEADER_ETAG,
     337             :                                            t->terms_etag));
     338          10 :   if (NULL != legal)
     339           0 :     GNUNET_break (MHD_YES ==
     340             :                   MHD_add_response_header (resp,
     341             :                                            TALER_TERMS_VERSION,
     342             :                                            legal->terms_version));
     343          10 :   GNUNET_break (MHD_YES ==
     344             :                 MHD_add_response_header (resp,
     345             :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     346             :                                          t->mime_type));
     347          10 :   GNUNET_break (MHD_YES ==
     348             :                 MHD_add_response_header (resp,
     349             :                                          MHD_HTTP_HEADER_CONTENT_LANGUAGE,
     350             :                                          t->language));
     351             :   {
     352             :     MHD_RESULT ret;
     353             : 
     354          10 :     ret = MHD_queue_response (conn,
     355             :                               t == &none
     356             :                               ? MHD_HTTP_NOT_IMPLEMENTED
     357             :                               : MHD_HTTP_OK,
     358             :                               resp);
     359          10 :     MHD_destroy_response (resp);
     360          10 :     return ret;
     361             :   }
     362             : }
     363             : 
     364             : 
     365             : /**
     366             :  * Load all the terms of service from @a path under language @a lang
     367             :  * from file @a name
     368             :  *
     369             :  * @param[in,out] legal where to write the result
     370             :  * @param path where the terms are found
     371             :  * @param lang which language directory to crawl
     372             :  * @param name specific file to access
     373             :  */
     374             : static void
     375           0 : load_terms (struct TALER_MHD_Legal *legal,
     376             :             const char *path,
     377             :             const char *lang,
     378             :             const char *name)
     379             : {
     380             :   static struct MimeMap
     381             :   {
     382             :     const char *ext;
     383             :     const char *mime;
     384             :     unsigned int priority;
     385             :   } mm[] = {
     386             :     { .ext = ".txt", .mime = "text/plain", .priority = 150 },
     387             :     { .ext = ".html", .mime = "text/html", .priority = 100 },
     388             :     { .ext = ".htm", .mime = "text/html", .priority = 99 },
     389             :     { .ext = ".md", .mime = "text/markdown", .priority = 50 },
     390             :     { .ext = ".pdf", .mime = "application/pdf", .priority = 25 },
     391             :     { .ext = ".jpg", .mime = "image/jpeg" },
     392             :     { .ext = ".jpeg", .mime = "image/jpeg" },
     393             :     { .ext = ".png", .mime = "image/png" },
     394             :     { .ext = ".gif", .mime = "image/gif" },
     395             :     { .ext = ".epub", .mime = "application/epub+zip", .priority = 10 },
     396             :     { .ext = ".xml", .mime = "text/xml", .priority = 10 },
     397             :     { .ext = NULL, .mime = NULL }
     398             :   };
     399           0 :   const char *ext = strrchr (name, '.');
     400             :   const char *mime;
     401             :   unsigned int priority;
     402             : 
     403           0 :   if (NULL == ext)
     404             :   {
     405           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     406             :                 "Unsupported file `%s' in directory `%s/%s': lacks extension\n",
     407             :                 name,
     408             :                 path,
     409             :                 lang);
     410           0 :     return;
     411             :   }
     412           0 :   if ( (NULL == legal->terms_version) ||
     413           0 :        (0 != strncmp (legal->terms_version,
     414             :                       name,
     415           0 :                       ext - name - 1)) )
     416             :   {
     417           0 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     418             :                 "Filename `%s' does not match Etag `%s' in directory `%s/%s'. Ignoring it.\n",
     419             :                 name,
     420             :                 legal->terms_version,
     421             :                 path,
     422             :                 lang);
     423           0 :     return;
     424             :   }
     425           0 :   mime = NULL;
     426           0 :   for (unsigned int i = 0; NULL != mm[i].ext; i++)
     427           0 :     if (0 == strcasecmp (mm[i].ext,
     428             :                          ext))
     429             :     {
     430           0 :       mime = mm[i].mime;
     431           0 :       priority = mm[i].priority;
     432           0 :       break;
     433             :     }
     434           0 :   if (NULL == mime)
     435             :   {
     436           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     437             :                 "Unsupported file extension `%s' of file `%s' in directory `%s/%s'\n",
     438             :                 ext,
     439             :                 name,
     440             :                 path,
     441             :                 lang);
     442           0 :     return;
     443             :   }
     444             :   /* try to read the file with the terms of service */
     445             :   {
     446             :     struct stat st;
     447             :     char *fn;
     448             :     int fd;
     449             : 
     450           0 :     GNUNET_asprintf (&fn,
     451             :                      "%s/%s/%s",
     452             :                      path,
     453             :                      lang,
     454             :                      name);
     455           0 :     fd = open (fn, O_RDONLY);
     456           0 :     if (-1 == fd)
     457             :     {
     458           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     459             :                                 "open",
     460             :                                 fn);
     461           0 :       GNUNET_free (fn);
     462           0 :       return;
     463             :     }
     464           0 :     if (0 != fstat (fd, &st))
     465             :     {
     466           0 :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     467             :                                 "fstat",
     468             :                                 fn);
     469           0 :       GNUNET_break (0 == close (fd));
     470           0 :       GNUNET_free (fn);
     471           0 :       return;
     472             :     }
     473             :     if (SIZE_MAX < ((unsigned long long) st.st_size))
     474             :     {
     475             :       GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     476             :                                 "fstat-size",
     477             :                                 fn);
     478             :       GNUNET_break (0 == close (fd));
     479             :       GNUNET_free (fn);
     480             :       return;
     481             :     }
     482           0 :     GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     483             :                 "Loading legal information from file `%s'\n",
     484             :                 fn);
     485             :     {
     486             :       void *buf;
     487             :       size_t bsize;
     488             : 
     489           0 :       bsize = (size_t) st.st_size;
     490           0 :       buf = mmap (NULL,
     491             :                   bsize,
     492             :                   PROT_READ,
     493             :                   MAP_SHARED,
     494             :                   fd,
     495             :                   0);
     496           0 :       if (MAP_FAILED == buf)
     497             :       {
     498           0 :         GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_WARNING,
     499             :                                   "mmap",
     500             :                                   fn);
     501           0 :         GNUNET_break (0 == close (fd));
     502           0 :         GNUNET_free (fn);
     503           0 :         return;
     504             :       }
     505           0 :       GNUNET_break (0 == close (fd));
     506           0 :       GNUNET_free (fn);
     507             : 
     508             :       /* insert into global list of terms of service */
     509             :       {
     510             :         struct Terms *t;
     511             :         struct GNUNET_HashCode hc;
     512             : 
     513           0 :         GNUNET_CRYPTO_hash (buf,
     514             :                             bsize,
     515             :                             &hc);
     516           0 :         t = GNUNET_new (struct Terms);
     517           0 :         t->mime_type = mime;
     518           0 :         t->terms = buf;
     519           0 :         t->language = GNUNET_strdup (lang);
     520           0 :         t->terms_size = bsize;
     521           0 :         t->priority = priority;
     522             :         t->terms_etag
     523           0 :           = GNUNET_STRINGS_data_to_string_alloc (&hc,
     524             :                                                  sizeof (hc) / 2);
     525           0 :         buf = GNUNET_memdup (t->terms,
     526             :                              t->terms_size);
     527           0 :         if (TALER_MHD_body_compress (&buf,
     528             :                                      &bsize))
     529             :         {
     530           0 :           t->compressed_terms = buf;
     531           0 :           t->compressed_terms_size = bsize;
     532             :         }
     533             :         else
     534             :         {
     535           0 :           GNUNET_free (buf);
     536             :         }
     537             :         {
     538           0 :           struct Terms *prev = NULL;
     539             : 
     540           0 :           for (struct Terms *pos = legal->terms_head;
     541           0 :                NULL != pos;
     542           0 :                pos = pos->next)
     543             :           {
     544           0 :             if (pos->priority < priority)
     545           0 :               break;
     546           0 :             prev = pos;
     547             :           }
     548           0 :           GNUNET_CONTAINER_DLL_insert_after (legal->terms_head,
     549             :                                              legal->terms_tail,
     550             :                                              prev,
     551             :                                              t);
     552             :         }
     553             :       }
     554             :     }
     555             :   }
     556             : }
     557             : 
     558             : 
     559             : /**
     560             :  * Load all the terms of service from @a path under language @a lang.
     561             :  *
     562             :  * @param[in,out] legal where to write the result
     563             :  * @param path where the terms are found
     564             :  * @param lang which language directory to crawl
     565             :  */
     566             : static void
     567           0 : load_language (struct TALER_MHD_Legal *legal,
     568             :                const char *path,
     569             :                const char *lang)
     570             : {
     571             :   char *dname;
     572             :   DIR *d;
     573             : 
     574           0 :   GNUNET_asprintf (&dname,
     575             :                    "%s/%s",
     576             :                    path,
     577             :                    lang);
     578           0 :   d = opendir (dname);
     579           0 :   if (NULL == d)
     580             :   {
     581           0 :     GNUNET_free (dname);
     582           0 :     return;
     583             :   }
     584           0 :   for (struct dirent *de = readdir (d);
     585           0 :        NULL != de;
     586           0 :        de = readdir (d))
     587             :   {
     588           0 :     const char *fn = de->d_name;
     589             : 
     590           0 :     if (fn[0] == '.')
     591           0 :       continue;
     592           0 :     load_terms (legal,
     593             :                 path,
     594             :                 lang,
     595             :                 fn);
     596             :   }
     597           0 :   GNUNET_break (0 == closedir (d));
     598           0 :   GNUNET_free (dname);
     599             : }
     600             : 
     601             : 
     602             : struct TALER_MHD_Legal *
     603          42 : TALER_MHD_legal_load (const struct GNUNET_CONFIGURATION_Handle *cfg,
     604             :                       const char *section,
     605             :                       const char *diroption,
     606             :                       const char *tagoption)
     607             : {
     608             :   struct TALER_MHD_Legal *legal;
     609             :   char *path;
     610             :   DIR *d;
     611             : 
     612          42 :   legal = GNUNET_new (struct TALER_MHD_Legal);
     613          42 :   if (GNUNET_OK !=
     614          42 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     615             :                                              section,
     616             :                                              tagoption,
     617             :                                              &legal->terms_version))
     618             :   {
     619           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     620             :                                section,
     621             :                                tagoption);
     622           0 :     GNUNET_free (legal);
     623           0 :     return NULL;
     624             :   }
     625          42 :   if (GNUNET_OK !=
     626          42 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     627             :                                                section,
     628             :                                                diroption,
     629             :                                                &path))
     630             :   {
     631           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     632             :                                section,
     633             :                                diroption);
     634           0 :     GNUNET_free (legal->terms_version);
     635           0 :     GNUNET_free (legal);
     636           0 :     return NULL;
     637             :   }
     638          42 :   d = opendir (path);
     639          42 :   if (NULL == d)
     640             :   {
     641          42 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
     642             :                                section,
     643             :                                diroption,
     644             :                                "Could not open directory");
     645          42 :     GNUNET_free (legal->terms_version);
     646          42 :     GNUNET_free (legal);
     647          42 :     GNUNET_free (path);
     648          42 :     return NULL;
     649             :   }
     650           0 :   for (struct dirent *de = readdir (d);
     651           0 :        NULL != de;
     652           0 :        de = readdir (d))
     653             :   {
     654           0 :     const char *lang = de->d_name;
     655             : 
     656           0 :     if (lang[0] == '.')
     657           0 :       continue;
     658           0 :     if (0 == strcmp (lang,
     659             :                      "locale"))
     660           0 :       continue;
     661           0 :     load_language (legal,
     662             :                    path,
     663             :                    lang);
     664             :   }
     665           0 :   GNUNET_break (0 == closedir (d));
     666           0 :   GNUNET_free (path);
     667           0 :   return legal;
     668             : }
     669             : 
     670             : 
     671             : void
     672           0 : TALER_MHD_legal_free (struct TALER_MHD_Legal *legal)
     673             : {
     674             :   struct Terms *t;
     675           0 :   if (NULL == legal)
     676           0 :     return;
     677           0 :   while (NULL != (t = legal->terms_head))
     678             :   {
     679           0 :     GNUNET_free (t->language);
     680           0 :     GNUNET_free (t->compressed_terms);
     681           0 :     if (0 != munmap (t->terms, t->terms_size))
     682           0 :       GNUNET_log_strerror (GNUNET_ERROR_TYPE_WARNING,
     683             :                            "munmap");
     684           0 :     GNUNET_CONTAINER_DLL_remove (legal->terms_head,
     685             :                                  legal->terms_tail,
     686             :                                  t);
     687           0 :     GNUNET_free (t->terms_etag);
     688           0 :     GNUNET_free (t);
     689             :   }
     690           0 :   GNUNET_free (legal->terms_version);
     691           0 :   GNUNET_free (legal);
     692             : }

Generated by: LCOV version 1.16