LCOV - code coverage report
Current view: top level - util - config.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 122 211 57.8 %
Date: 2025-07-16 18:44:17 Functions: 7 7 100.0 %

          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       94146 : 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       94146 :   if (GNUNET_OK !=
      36       94146 :       GNUNET_CONFIGURATION_get_value_string (cfg,
      37             :                                              section,
      38             :                                              option,
      39             :                                              &str))
      40             :   {
      41             :     /* may be OK! */
      42          51 :     return GNUNET_NO;
      43             :   }
      44       94095 :   if (GNUNET_OK !=
      45       94095 :       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       94095 :   GNUNET_free (str);
      56       94095 :   return GNUNET_OK;
      57             : }
      58             : 
      59             : 
      60             : enum GNUNET_GenericReturnValue
      61       18660 : 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       18660 :   if (GNUNET_OK !=
      67       18660 :       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       18660 :   if (GNUNET_OK !=
      79       18660 :       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       18660 :   if (GNUNET_OK !=
      91       18660 :       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       18660 :   if (GNUNET_OK !=
     103       18660 :       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       18660 :   if (GNUNET_OK !=
     115       18660 :       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       18660 :   return GNUNET_OK;
     125             : }
     126             : 
     127             : 
     128             : enum GNUNET_GenericReturnValue
     129        2327 : TALER_config_get_currency (const struct GNUNET_CONFIGURATION_Handle *cfg,
     130             :                            const char *section,
     131             :                            char **currency)
     132             : {
     133             :   size_t slen;
     134             : 
     135        2327 :   if (GNUNET_OK !=
     136        2327 :       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        2327 :   slen = strlen (*currency);
     147        2327 :   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       20702 :   for (size_t i = 0; i<slen; i++)
     158       18375 :     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        2327 :   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        1081 : parse_currencies_cb (void *cls,
     211             :                      const char *section)
     212             : {
     213        1081 :   struct CurrencyParserContext *cpc = cls;
     214             :   struct TALER_CurrencySpecification *cspec;
     215             :   unsigned long long num;
     216             :   char *str;
     217             : 
     218        1081 :   if (cpc->failure)
     219         966 :     return;
     220        1081 :   if (0 != strncasecmp (section,
     221             :                         "currency-",
     222             :                         strlen ("currency-")))
     223         851 :     return; /* not interesting */
     224         230 :   if (GNUNET_YES !=
     225         230 :       GNUNET_CONFIGURATION_get_value_yesno (cpc->cfg,
     226             :                                             section,
     227             :                                             "ENABLED"))
     228         115 :     return; /* disabled */
     229         115 :   if (cpc->len_cspecs == cpc->num_currencies)
     230             :   {
     231          46 :     GNUNET_array_grow (cpc->cspecs,
     232             :                        cpc->len_cspecs,
     233             :                        cpc->len_cspecs * 2 + 4);
     234             :   }
     235         115 :   cspec = &cpc->cspecs[cpc->num_currencies++];
     236         115 :   if (GNUNET_OK !=
     237         115 :       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         115 :   if (GNUNET_OK !=
     249         115 :       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         115 :   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         115 :   GNUNET_assert (strlen (str) < TALER_CURRENCY_LEN);
     265         115 :   strcpy (cspec->currency,
     266             :           str);
     267         115 :   GNUNET_free (str);
     268             : 
     269         115 :   if (GNUNET_OK !=
     270         115 :       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         115 :   cspec->name = str;
     282             : 
     283         115 :   if (GNUNET_OK !=
     284         115 :       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
     285             :                                              section,
     286             :                                              "COMMON_AMOUNTS",
     287             :                                              &str))
     288             :   {
     289          10 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
     290             :                                section,
     291             :                                "COMMON_AMOUNTS");
     292             :   }
     293             :   else
     294             :   {
     295         105 :     for (const char *tok = strtok (str,
     296             :                                    " ");
     297         525 :          NULL != tok;
     298         420 :          tok = strtok (NULL,
     299             :                        " "))
     300             :     {
     301             :       struct TALER_Amount val;
     302             : 
     303         420 :       if (GNUNET_OK !=
     304         420 :           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         420 :       if (0 != strcasecmp (val.currency,
     316         420 :                            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         420 :       GNUNET_array_append (cspec->common_amounts,
     327             :                            cspec->num_common_amounts,
     328             :                            val);
     329             :     }
     330         105 :     GNUNET_free (str);
     331             :   }
     332         115 :   if (GNUNET_OK !=
     333         115 :       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         115 :   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         115 :   cspec->num_fractional_input_digits = num;
     354         115 :   if (GNUNET_OK !=
     355         115 :       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         115 :   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         115 :   cspec->num_fractional_normal_digits = num;
     376         115 :   if (GNUNET_OK !=
     377         115 :       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         115 :   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         115 :   cspec->num_fractional_trailing_zero_digits = num;
     398             : 
     399         115 :   if (GNUNET_OK !=
     400         115 :       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         115 :     cspec->map_alt_unit_names = json_loads (str,
     415             :                                             JSON_REJECT_DUPLICATES,
     416             :                                             &err);
     417         115 :     GNUNET_free (str);
     418         115 :     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         115 :   if (GNUNET_OK !=
     429         115 :       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         177 : 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         177 :   bool zf = false;
     450             : 
     451         177 :   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         445 :   json_object_foreach ((json_t *) map, str, val)
     458             :   {
     459             :     int idx;
     460             :     char dummy;
     461             : 
     462         268 :     if ( (1 != sscanf (str,
     463             :                        "%d%c",
     464             :                        &idx,
     465         268 :                        &dummy)) ||
     466         268 :          (idx < -12) ||
     467         268 :          (idx > 24) ||
     468         268 :          (! 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         268 :     if (0 == idx)
     476         177 :       zf = true;
     477             :   }
     478         177 :   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         177 :   return GNUNET_OK;
     485             : }
     486             : 
     487             : 
     488             : enum GNUNET_GenericReturnValue
     489          23 : 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          23 :   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          23 :   GNUNET_CONFIGURATION_iterate_sections (cfg,
     504             :                                          &parse_currencies_cb,
     505             :                                          &cpc);
     506          23 :   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          23 :   if (NULL != main_currency)
     515             :   {
     516          23 :     struct TALER_CurrencySpecification *mspec = NULL;
     517         100 :     for (unsigned int i = 0; i<cpc.num_currencies; i++)
     518             :     {
     519             :       struct TALER_CurrencySpecification *cspec;
     520             : 
     521         100 :       cspec = &cpc.cspecs[i];
     522         100 :       if (0 == strcmp (main_currency,
     523         100 :                        cspec->currency))
     524             :       {
     525          23 :         mspec = cspec;
     526          23 :         break;
     527             :       }
     528             :     }
     529          23 :     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          23 :   GNUNET_array_grow (cpc.cspecs,
     555             :                      cpc.len_cspecs,
     556             :                      cpc.num_currencies);
     557          23 :   *num_currencies = cpc.num_currencies;
     558          23 :   *cspecs = cpc.cspecs;
     559          23 :   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          23 :   return GNUNET_OK;
     566             : }
     567             : 
     568             : 
     569             : void
     570          21 : TALER_CONFIG_free_currencies (
     571             :   unsigned int num_currencies,
     572             :   struct TALER_CurrencySpecification cspecs[static num_currencies])
     573          21 : {
     574         126 :   for (unsigned int i = 0; i<num_currencies; i++)
     575             :   {
     576         105 :     struct TALER_CurrencySpecification *cspec = &cspecs[i];
     577             : 
     578         105 :     GNUNET_free (cspec->name);
     579         105 :     json_decref (cspec->map_alt_unit_names);
     580         105 :     GNUNET_array_grow (cspec->common_amounts,
     581             :                        cspec->num_common_amounts,
     582             :                        0);
     583             :   }
     584          21 :   GNUNET_array_grow (cspecs,
     585             :                      num_currencies,
     586             :                      0);
     587          21 : }

Generated by: LCOV version 1.16