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

Generated by: LCOV version 1.16