LCOV - code coverage report
Current view: top level - util - config.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 54.6 % 218 119
Test Date: 2026-09-09 15:11:34 Functions: 87.5 % 8 7

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2014-2023 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU 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 General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file config.c
      18              :  * @brief configuration parsing functions for Taler-specific data types
      19              :  * @author Florian Dold
      20              :  * @author Benedikt Mueller
      21              :  */
      22              : #include "platform.h"  /* UNNECESSARY? */
      23              : #include "taler/taler_util.h"
      24              : #include <gnunet/gnunet_json_lib.h>
      25              : 
      26              : 
      27              : enum GNUNET_GenericReturnValue
      28         4919 : TALER_config_get_amount (const struct GNUNET_CONFIGURATION_Handle *cfg,
      29              :                          const char *section,
      30              :                          const char *option,
      31              :                          struct TALER_Amount *denom)
      32              : {
      33              :   char *str;
      34              : 
      35         4919 :   if (GNUNET_OK !=
      36         4919 :       GNUNET_CONFIGURATION_get_value_string (cfg,
      37              :                                              section,
      38              :                                              option,
      39              :                                              &str))
      40              :   {
      41              :     /* may be OK! */
      42           21 :     return GNUNET_NO;
      43              :   }
      44         4898 :   if (GNUNET_OK !=
      45         4898 :       TALER_string_to_amount (str,
      46              :                               denom))
      47              :   {
      48            0 :     GNUNET_free (str);
      49            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
      50              :                                section,
      51              :                                option,
      52              :                                "invalid amount");
      53            0 :     return GNUNET_SYSERR;
      54              :   }
      55         4898 :   GNUNET_free (str);
      56         4898 :   return GNUNET_OK;
      57              : }
      58              : 
      59              : 
      60              : enum GNUNET_GenericReturnValue
      61            0 : TALER_config_get_amount_list (const struct GNUNET_CONFIGURATION_Handle *cfg,
      62              :                               const char *section,
      63              :                               const char *option,
      64              :                               struct TALER_AmountList *al)
      65              : {
      66              :   char *str;
      67              : 
      68            0 :   al->tal = NULL;
      69            0 :   al->tal_len = 0;
      70            0 :   if (GNUNET_OK !=
      71            0 :       GNUNET_CONFIGURATION_get_value_string (cfg,
      72              :                                              section,
      73              :                                              option,
      74              :                                              &str))
      75              :   {
      76              :     /* may be OK! */
      77            0 :     return GNUNET_NO;
      78              :   }
      79            0 :   if (GNUNET_OK !=
      80            0 :       TALER_string_to_amount_list (str,
      81              :                                    al))
      82              :   {
      83            0 :     GNUNET_free (str);
      84            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
      85              :                                section,
      86              :                                option,
      87              :                                "invalid list of amounts");
      88            0 :     return GNUNET_SYSERR;
      89              :   }
      90            0 :   GNUNET_free (str);
      91            0 :   return GNUNET_OK;
      92              : }
      93              : 
      94              : 
      95              : enum GNUNET_GenericReturnValue
      96          949 : TALER_config_get_denom_fees (const struct GNUNET_CONFIGURATION_Handle *cfg,
      97              :                              const char *currency,
      98              :                              const char *section,
      99              :                              struct TALER_DenomFeeSet *fees)
     100              : {
     101          949 :   if (GNUNET_OK !=
     102          949 :       TALER_config_get_amount (cfg,
     103              :                                section,
     104              :                                "FEE_WITHDRAW",
     105              :                                &fees->withdraw))
     106              :   {
     107            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     108              :                                section,
     109              :                                "FEE_WITHDRAW",
     110              :                                "amount required");
     111            0 :     return GNUNET_SYSERR;
     112              :   }
     113          949 :   if (GNUNET_OK !=
     114          949 :       TALER_config_get_amount (cfg,
     115              :                                section,
     116              :                                "FEE_DEPOSIT",
     117              :                                &fees->deposit))
     118              :   {
     119            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     120              :                                "Need amount for option `%s' in section `%s'\n",
     121              :                                "FEE_DEPOSIT",
     122              :                                section);
     123            0 :     return GNUNET_SYSERR;
     124              :   }
     125          949 :   if (GNUNET_OK !=
     126          949 :       TALER_config_get_amount (cfg,
     127              :                                section,
     128              :                                "FEE_REFRESH",
     129              :                                &fees->refresh))
     130              :   {
     131            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     132              :                                "Need amount for option `%s' in section `%s'\n",
     133              :                                "FEE_REFRESH",
     134              :                                section);
     135            0 :     return GNUNET_SYSERR;
     136              :   }
     137          949 :   if (GNUNET_OK !=
     138          949 :       TALER_config_get_amount (cfg,
     139              :                                section,
     140              :                                "FEE_REFUND",
     141              :                                &fees->refund))
     142              :   {
     143            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     144              :                                "Need amount for option `%s' in section `%s'\n",
     145              :                                "FEE_REFUND",
     146              :                                section);
     147            0 :     return GNUNET_SYSERR;
     148              :   }
     149          949 :   if (GNUNET_OK !=
     150          949 :       TALER_denom_fee_check_currency (currency,
     151              :                                       fees))
     152              :   {
     153            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     154              :                 "Need fee amounts in section `%s' to use currency `%s'\n",
     155              :                 section,
     156              :                 currency);
     157            0 :     return GNUNET_SYSERR;
     158              :   }
     159          949 :   return GNUNET_OK;
     160              : }
     161              : 
     162              : 
     163              : enum GNUNET_GenericReturnValue
     164          485 : TALER_config_get_currency (const struct GNUNET_CONFIGURATION_Handle *cfg,
     165              :                            const char *section,
     166              :                            char **currency)
     167              : {
     168          485 :   if (GNUNET_OK !=
     169          485 :       GNUNET_CONFIGURATION_get_value_string (cfg,
     170              :                                              section,
     171              :                                              "CURRENCY",
     172              :                                              currency))
     173              :   {
     174            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     175              :                                section,
     176              :                                "CURRENCY");
     177            0 :     return GNUNET_SYSERR;
     178              :   }
     179          485 :   if (GNUNET_OK !=
     180          485 :       TALER_check_currency (*currency))
     181              :   {
     182            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     183              :                 "Currency `%s' must only use characters from the A-Z range.",
     184              :                 *currency);
     185            0 :     GNUNET_free (*currency);
     186            0 :     *currency = NULL;
     187            0 :     return GNUNET_SYSERR;
     188              :   }
     189          485 :   return GNUNET_OK;
     190              : }
     191              : 
     192              : 
     193              : /**
     194              :  * Closure for #parse_currencies_cb().
     195              :  */
     196              : struct CurrencyParserContext
     197              : {
     198              :   /**
     199              :    * Current offset in @e cspecs.
     200              :    */
     201              :   unsigned int num_currencies;
     202              : 
     203              :   /**
     204              :    * Length of the @e cspecs array.
     205              :    */
     206              :   unsigned int len_cspecs;
     207              : 
     208              :   /**
     209              :    * Array of currency specifications (see DD 51).
     210              :    */
     211              :   struct TALER_CurrencySpecification *cspecs;
     212              : 
     213              :   /**
     214              :    * Configuration we are parsing.
     215              :    */
     216              :   const struct GNUNET_CONFIGURATION_Handle *cfg;
     217              : 
     218              :   /**
     219              :    * Set to true if the configuration was malformed.
     220              :    */
     221              :   bool failure;
     222              : };
     223              : 
     224              : 
     225              : /**
     226              :  * Function to iterate over section.
     227              :  *
     228              :  * @param cls closure with a `struct CurrencyParserContext *`
     229              :  * @param section name of the section
     230              :  */
     231              : static void
     232          892 : parse_currencies_cb (void *cls,
     233              :                      const char *section)
     234              : {
     235          892 :   struct CurrencyParserContext *cpc = cls;
     236              :   struct TALER_CurrencySpecification *cspec;
     237              :   unsigned long long num;
     238              :   char *str;
     239              : 
     240          892 :   if (cpc->failure)
     241          802 :     return;
     242          892 :   if (0 != strncasecmp (section,
     243              :                         "currency-",
     244              :                         strlen ("currency-")))
     245          712 :     return; /* not interesting */
     246          180 :   if (GNUNET_YES !=
     247          180 :       GNUNET_CONFIGURATION_get_value_yesno (cpc->cfg,
     248              :                                             section,
     249              :                                             "ENABLED"))
     250           90 :     return; /* disabled */
     251           90 :   if (cpc->len_cspecs == cpc->num_currencies)
     252              :   {
     253           36 :     GNUNET_array_grow (cpc->cspecs,
     254              :                        cpc->len_cspecs,
     255              :                        cpc->len_cspecs * 2 + 4);
     256              :   }
     257           90 :   cspec = &cpc->cspecs[cpc->num_currencies++];
     258           90 :   if (GNUNET_OK !=
     259           90 :       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
     260              :                                              section,
     261              :                                              "CODE",
     262              :                                              &str))
     263              :   {
     264            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     265              :                                section,
     266              :                                "CODE");
     267            0 :     cpc->failure = true;
     268            0 :     return;
     269              :   }
     270           90 :   if (GNUNET_OK !=
     271           90 :       TALER_check_currency (str))
     272              :   {
     273            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     274              :                                section,
     275              :                                "CODE",
     276              :                                "Currency code name given is invalid");
     277            0 :     cpc->failure = true;
     278            0 :     GNUNET_free (str);
     279            0 :     return;
     280              :   }
     281           90 :   memset (cspec->currency,
     282              :           0,
     283              :           sizeof (cspec->currency));
     284              :   /* Already checked in TALER_check_currency(), repeated here
     285              :      just to make static analysis happy */
     286           90 :   GNUNET_assert (strlen (str) < TALER_CURRENCY_LEN);
     287           90 :   strcpy (cspec->currency,
     288              :           str);
     289           90 :   GNUNET_free (str);
     290              : 
     291           90 :   if (GNUNET_OK !=
     292           90 :       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
     293              :                                              section,
     294              :                                              "NAME",
     295              :                                              &str))
     296              :   {
     297            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     298              :                                section,
     299              :                                "NAME");
     300            0 :     cpc->failure = true;
     301            0 :     return;
     302              :   }
     303           90 :   cspec->name = str;
     304              : 
     305           90 :   if (GNUNET_OK !=
     306           90 :       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
     307              :                                              section,
     308              :                                              "COMMON_AMOUNTS",
     309              :                                              &str))
     310              :   {
     311            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     312              :                                section,
     313              :                                "COMMON_AMOUNTS");
     314              :   }
     315              :   else
     316              :   {
     317           90 :     for (const char *tok = strtok (str,
     318              :                                    " ");
     319          450 :          NULL != tok;
     320          360 :          tok = strtok (NULL,
     321              :                        " "))
     322              :     {
     323              :       struct TALER_Amount val;
     324              : 
     325          360 :       if (GNUNET_OK !=
     326          360 :           TALER_string_to_amount (tok,
     327              :                                   &val))
     328              :       {
     329            0 :         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     330              :                                    section,
     331              :                                    "COMMON_AMOUNTS",
     332              :                                    tok);
     333            0 :         GNUNET_free (str);
     334            0 :         cpc->failure = true;
     335            0 :         return;
     336              :       }
     337          360 :       if (0 != strcasecmp (val.currency,
     338          360 :                            cspec->currency))
     339              :       {
     340            0 :         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     341              :                                    section,
     342              :                                    "COMMON_AMOUNTS",
     343              :                                    "currency mismatch");
     344            0 :         GNUNET_free (str);
     345            0 :         cpc->failure = true;
     346            0 :         return;
     347              :       }
     348          360 :       GNUNET_array_append (cspec->common_amounts,
     349              :                            cspec->num_common_amounts,
     350              :                            val);
     351              :     }
     352           90 :     GNUNET_free (str);
     353              :   }
     354           90 :   if (GNUNET_OK !=
     355           90 :       GNUNET_CONFIGURATION_get_value_number (cpc->cfg,
     356              :                                              section,
     357              :                                              "FRACTIONAL_INPUT_DIGITS",
     358              :                                              &num))
     359              :   {
     360            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     361              :                                section,
     362              :                                "FRACTIONAL_INPUT_DIGITS");
     363            0 :     cpc->failure = true;
     364            0 :     return;
     365              :   }
     366           90 :   if (num > TALER_AMOUNT_FRAC_LEN)
     367              :   {
     368            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     369              :                                section,
     370              :                                "FRACTIONAL_INPUT_DIGITS",
     371              :                                "Number given is too big");
     372            0 :     cpc->failure = true;
     373            0 :     return;
     374              :   }
     375           90 :   cspec->num_fractional_input_digits = num;
     376           90 :   if (GNUNET_OK !=
     377           90 :       GNUNET_CONFIGURATION_get_value_number (cpc->cfg,
     378              :                                              section,
     379              :                                              "FRACTIONAL_NORMAL_DIGITS",
     380              :                                              &num))
     381              :   {
     382            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     383              :                                section,
     384              :                                "FRACTIONAL_NORMAL_DIGITS");
     385            0 :     cpc->failure = true;
     386            0 :     return;
     387              :   }
     388           90 :   if (num > TALER_AMOUNT_FRAC_LEN)
     389              :   {
     390            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     391              :                                section,
     392              :                                "FRACTIONAL_NORMAL_DIGITS",
     393              :                                "Number given is too big");
     394            0 :     cpc->failure = true;
     395            0 :     return;
     396              :   }
     397           90 :   cspec->num_fractional_normal_digits = num;
     398           90 :   if (GNUNET_OK !=
     399           90 :       GNUNET_CONFIGURATION_get_value_number (cpc->cfg,
     400              :                                              section,
     401              :                                              "FRACTIONAL_TRAILING_ZERO_DIGITS",
     402              :                                              &num))
     403              :   {
     404            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     405              :                                section,
     406              :                                "FRACTIONAL_TRAILING_ZERO_DIGITS");
     407            0 :     cpc->failure = true;
     408            0 :     return;
     409              :   }
     410           90 :   if (num > TALER_AMOUNT_FRAC_LEN)
     411              :   {
     412            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     413              :                                section,
     414              :                                "FRACTIONAL_TRAILING_ZERO_DIGITS",
     415              :                                "Number given is too big");
     416            0 :     cpc->failure = true;
     417            0 :     return;
     418              :   }
     419           90 :   cspec->num_fractional_trailing_zero_digits = num;
     420              : 
     421           90 :   if (GNUNET_OK !=
     422           90 :       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
     423              :                                              section,
     424              :                                              "ALT_UNIT_NAMES",
     425              :                                              &str))
     426              :   {
     427            0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     428              :                                section,
     429              :                                "ALT_UNIT_NAMES");
     430            0 :     cpc->failure = true;
     431            0 :     return;
     432              :   }
     433              :   {
     434              :     json_error_t err;
     435              : 
     436           90 :     cspec->map_alt_unit_names = json_loads (str,
     437              :                                             JSON_REJECT_DUPLICATES,
     438              :                                             &err);
     439           90 :     GNUNET_free (str);
     440           90 :     if (NULL == cspec->map_alt_unit_names)
     441              :     {
     442            0 :       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     443              :                                  section,
     444              :                                  "ALT_UNIT_NAMES",
     445              :                                  err.text);
     446            0 :       cpc->failure = true;
     447            0 :       return;
     448              :     }
     449              :   }
     450           90 :   if (GNUNET_OK !=
     451           90 :       TALER_check_currency_scale_map (cspec->map_alt_unit_names))
     452              :   {
     453            0 :     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     454              :                                section,
     455              :                                "ALT_UNIT_NAMES",
     456              :                                "invalid map entry detected");
     457            0 :     cpc->failure = true;
     458            0 :     json_decref (cspec->map_alt_unit_names);
     459            0 :     cspec->map_alt_unit_names = NULL;
     460            0 :     return;
     461              :   }
     462              : }
     463              : 
     464              : 
     465              : enum GNUNET_GenericReturnValue
     466          132 : TALER_check_currency_scale_map (const json_t *map)
     467              : {
     468              :   /* validate map only maps from decimal numbers to strings! */
     469              :   const char *str;
     470              :   const json_t *val;
     471          132 :   bool zf = false;
     472              : 
     473          132 :   if (! json_is_object (map))
     474              :   {
     475            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     476              :                 "Object required for currency scale map\n");
     477            0 :     return GNUNET_SYSERR;
     478              :   }
     479          318 :   json_object_foreach ((json_t *) map, str, val)
     480              :   {
     481              :     int idx;
     482              :     char dummy;
     483              : 
     484          186 :     if ( (1 != sscanf (str,
     485              :                        "%d%c",
     486              :                        &idx,
     487          186 :                        &dummy)) ||
     488          186 :          (idx < -12) ||
     489          186 :          (idx > 24) ||
     490          186 :          (! json_is_string (val) ) )
     491              :     {
     492            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     493              :                   "Invalid entry `%s' in currency scale map\n",
     494              :                   str);
     495            0 :       return GNUNET_SYSERR;
     496              :     }
     497          186 :     if (0 == idx)
     498          132 :       zf = true;
     499              :   }
     500          132 :   if (! zf)
     501              :   {
     502            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     503              :                 "Entry for 0 missing in currency scale map\n");
     504            0 :     return GNUNET_SYSERR;
     505              :   }
     506          132 :   return GNUNET_OK;
     507              : }
     508              : 
     509              : 
     510              : enum GNUNET_GenericReturnValue
     511           18 : TALER_CONFIG_parse_currencies (const struct GNUNET_CONFIGURATION_Handle *cfg,
     512              :                                const char *main_currency,
     513              :                                unsigned int *num_currencies,
     514              :                                struct TALER_CurrencySpecification **cspecs)
     515              : {
     516           18 :   struct CurrencyParserContext cpc = {
     517              :     .cfg = cfg
     518              :   };
     519              :   static struct TALER_CurrencySpecification defspec = {
     520              :     .num_fractional_input_digits = 2,
     521              :     .num_fractional_normal_digits = 2,
     522              :     .num_fractional_trailing_zero_digits = 2
     523              :   };
     524              : 
     525           18 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
     526              :                                          &parse_currencies_cb,
     527              :                                          &cpc);
     528           18 :   if (cpc.failure)
     529              :   {
     530            0 :     TALER_CONFIG_free_currencies (cpc.num_currencies,
     531              :                                   cpc.cspecs);
     532            0 :     return GNUNET_SYSERR;
     533              :   }
     534              :   /* Make sure that there is some sane fallback for the main currency */
     535           18 :   if (NULL != main_currency)
     536              :   {
     537           18 :     struct TALER_CurrencySpecification *mspec = NULL;
     538           88 :     for (unsigned int i = 0; i<cpc.num_currencies; i++)
     539              :     {
     540              :       struct TALER_CurrencySpecification *cspec;
     541              : 
     542           88 :       cspec = &cpc.cspecs[i];
     543           88 :       if (0 == strcasecmp (main_currency,
     544           88 :                            cspec->currency))
     545              :       {
     546           18 :         mspec = cspec;
     547           18 :         break;
     548              :       }
     549              :     }
     550           18 :     if (NULL == mspec)
     551              :     {
     552            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     553              :                   "Lacking enabled currency specification for main currency %s, using fallback currency specification.\n",
     554              :                   main_currency);
     555            0 :       if (cpc.len_cspecs == cpc.num_currencies)
     556              :       {
     557            0 :         GNUNET_array_grow (cpc.cspecs,
     558              :                            cpc.len_cspecs,
     559              :                            cpc.len_cspecs + 1);
     560              :       }
     561            0 :       mspec = &cpc.cspecs[cpc.num_currencies++];
     562            0 :       *mspec = defspec;
     563            0 :       GNUNET_assert (strlen (main_currency) < TALER_CURRENCY_LEN);
     564            0 :       strcpy (mspec->currency,
     565              :               main_currency);
     566              :       mspec->map_alt_unit_names
     567            0 :         = GNUNET_JSON_PACK (
     568              :             GNUNET_JSON_pack_string ("0",
     569              :                                      main_currency)
     570              :             );
     571            0 :       mspec->name = GNUNET_strdup (main_currency);
     572              :     }
     573              :   }
     574              :   /* cspecs might've been overgrown, grow back to minimum size */
     575           18 :   GNUNET_array_grow (cpc.cspecs,
     576              :                      cpc.len_cspecs,
     577              :                      cpc.num_currencies);
     578           18 :   *num_currencies = cpc.num_currencies;
     579           18 :   *cspecs = cpc.cspecs;
     580           18 :   if (0 == *num_currencies)
     581              :   {
     582            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     583              :                 "No currency formatting specification found! Please check your installation!\n");
     584            0 :     return GNUNET_NO;
     585              :   }
     586           18 :   return GNUNET_OK;
     587              : }
     588              : 
     589              : 
     590              : void
     591           18 : TALER_CONFIG_free_currencies (
     592              :   unsigned int num_currencies,
     593              :   struct TALER_CurrencySpecification cspecs[static num_currencies])
     594           18 : {
     595          108 :   for (unsigned int i = 0; i<num_currencies; i++)
     596              :   {
     597           90 :     struct TALER_CurrencySpecification *cspec = &cspecs[i];
     598              : 
     599           90 :     GNUNET_free (cspec->name);
     600           90 :     json_decref (cspec->map_alt_unit_names);
     601           90 :     GNUNET_array_grow (cspec->common_amounts,
     602              :                        cspec->num_common_amounts,
     603              :                        0);
     604              :   }
     605           18 :   GNUNET_array_grow (cspecs,
     606              :                      num_currencies,
     607              :                      0);
     608           18 : }
        

Generated by: LCOV version 2.0-1