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

Generated by: LCOV version 2.0-1