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

Generated by: LCOV version 1.16