exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

config.c (18705B)


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