LCOV - code coverage report
Current view: top level - mhd - mhd_legal.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 42 216 19.4 %
Date: 2025-08-30 09:28:00 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 ( (TALER_MHD_CT_DEFLATE ==
     284          10 :         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          10 :   if (NULL == resp)
     302             :   {
     303             :     /* could not generate compressed response, return uncompressed */
     304          10 :     resp = MHD_create_response_from_buffer (t->terms_size,
     305             :                                             (void *) t->terms,
     306             :                                             MHD_RESPMEM_PERSISTENT);
     307             :   }
     308          10 :   TALER_MHD_add_global_headers (resp,
     309             :                                 true);
     310          10 :   GNUNET_break (MHD_YES ==
     311             :                 MHD_add_response_header (resp,
     312             :                                          MHD_HTTP_HEADER_EXPIRES,
     313             :                                          dat));
     314          10 :   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          10 :   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          10 :   GNUNET_break (MHD_YES ==
     331             :                 MHD_add_response_header (resp,
     332             :                                          MHD_HTTP_HEADER_CACHE_CONTROL,
     333             :                                          "public,max-age=864000"));
     334          10 :   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          10 :   if (NULL != legal)
     340           0 :     GNUNET_break (MHD_YES ==
     341             :                   MHD_add_response_header (resp,
     342             :                                            TALER_TERMS_VERSION,
     343             :                                            legal->terms_version));
     344          10 :   GNUNET_break (MHD_YES ==
     345             :                 MHD_add_response_header (resp,
     346             :                                          MHD_HTTP_HEADER_CONTENT_TYPE,
     347             :                                          t->mime_type));
     348          10 :   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          10 :     ret = MHD_queue_response (conn,
     356             :                               t == &none
     357             :                               ? MHD_HTTP_NOT_IMPLEMENTED
     358             :                               : MHD_HTTP_OK,
     359             :                               resp);
     360          10 :     MHD_destroy_response (resp);
     361          10 :     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          42 : 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          42 :   legal = GNUNET_new (struct TALER_MHD_Legal);
     614          42 :   if (GNUNET_OK !=
     615          42 :       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          42 :   if (GNUNET_OK !=
     627          42 :       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          42 :   d = opendir (path);
     640          42 :   if (NULL == d)
     641             :   {
     642          42 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_WARNING,
     643             :                                section,
     644             :                                diroption,
     645             :                                "Could not open directory");
     646          42 :     GNUNET_free (legal->terms_version);
     647          42 :     GNUNET_free (legal);
     648          42 :     GNUNET_free (path);
     649          42 :     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 1.16