exchange

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

config.c (18126B)


      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 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_denom_fees (const struct GNUNET_CONFIGURATION_Handle *cfg,
     62                              const char *currency,
     63                              const char *section,
     64                              struct TALER_DenomFeeSet *fees)
     65 {
     66   if (GNUNET_OK !=
     67       TALER_config_get_amount (cfg,
     68                                section,
     69                                "FEE_WITHDRAW",
     70                                &fees->withdraw))
     71   {
     72     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     73                                "Need amount for option `%s' in section `%s'\n",
     74                                "FEE_WITHDRAW",
     75                                section);
     76     return GNUNET_SYSERR;
     77   }
     78   if (GNUNET_OK !=
     79       TALER_config_get_amount (cfg,
     80                                section,
     81                                "FEE_DEPOSIT",
     82                                &fees->deposit))
     83   {
     84     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     85                                "Need amount for option `%s' in section `%s'\n",
     86                                "FEE_DEPOSIT",
     87                                section);
     88     return GNUNET_SYSERR;
     89   }
     90   if (GNUNET_OK !=
     91       TALER_config_get_amount (cfg,
     92                                section,
     93                                "FEE_REFRESH",
     94                                &fees->refresh))
     95   {
     96     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
     97                                "Need amount for option `%s' in section `%s'\n",
     98                                "FEE_REFRESH",
     99                                section);
    100     return GNUNET_SYSERR;
    101   }
    102   if (GNUNET_OK !=
    103       TALER_config_get_amount (cfg,
    104                                section,
    105                                "FEE_REFUND",
    106                                &fees->refund))
    107   {
    108     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    109                                "Need amount for option `%s' in section `%s'\n",
    110                                "FEE_REFUND",
    111                                section);
    112     return GNUNET_SYSERR;
    113   }
    114   if (GNUNET_OK !=
    115       TALER_denom_fee_check_currency (currency,
    116                                       fees))
    117   {
    118     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    119                 "Need fee amounts in section `%s' to use currency `%s'\n",
    120                 section,
    121                 currency);
    122     return GNUNET_SYSERR;
    123   }
    124   return GNUNET_OK;
    125 }
    126 
    127 
    128 enum GNUNET_GenericReturnValue
    129 TALER_config_get_currency (const struct GNUNET_CONFIGURATION_Handle *cfg,
    130                            const char *section,
    131                            char **currency)
    132 {
    133   size_t slen;
    134 
    135   if (GNUNET_OK !=
    136       GNUNET_CONFIGURATION_get_value_string (cfg,
    137                                              section,
    138                                              "CURRENCY",
    139                                              currency))
    140   {
    141     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    142                                section,
    143                                "CURRENCY");
    144     return GNUNET_SYSERR;
    145   }
    146   slen = strlen (*currency);
    147   if (slen >= TALER_CURRENCY_LEN)
    148   {
    149     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     GNUNET_free (*currency);
    154     *currency = NULL;
    155     return GNUNET_SYSERR;
    156   }
    157   for (size_t i = 0; i<slen; i++)
    158     if (! isalpha ((unsigned char) (*currency)[i]))
    159     {
    160       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    161                   "Currency `%s' must only use characters from the A-Z range.",
    162                   *currency);
    163       GNUNET_free (*currency);
    164       *currency = NULL;
    165       return GNUNET_SYSERR;
    166     }
    167   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 parse_currencies_cb (void *cls,
    211                      const char *section)
    212 {
    213   struct CurrencyParserContext *cpc = cls;
    214   struct TALER_CurrencySpecification *cspec;
    215   unsigned long long num;
    216   char *str;
    217 
    218   if (cpc->failure)
    219     return;
    220   if (0 != strncasecmp (section,
    221                         "currency-",
    222                         strlen ("currency-")))
    223     return; /* not interesting */
    224   if (GNUNET_YES !=
    225       GNUNET_CONFIGURATION_get_value_yesno (cpc->cfg,
    226                                             section,
    227                                             "ENABLED"))
    228     return; /* disabled */
    229   if (cpc->len_cspecs == cpc->num_currencies)
    230   {
    231     GNUNET_array_grow (cpc->cspecs,
    232                        cpc->len_cspecs,
    233                        cpc->len_cspecs * 2 + 4);
    234   }
    235   cspec = &cpc->cspecs[cpc->num_currencies++];
    236   if (GNUNET_OK !=
    237       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
    238                                              section,
    239                                              "CODE",
    240                                              &str))
    241   {
    242     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    243                                section,
    244                                "CODE");
    245     cpc->failure = true;
    246     return;
    247   }
    248   if (GNUNET_OK !=
    249       TALER_check_currency (str))
    250   {
    251     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    252                                section,
    253                                "CODE",
    254                                "Currency code name given is invalid");
    255     cpc->failure = true;
    256     GNUNET_free (str);
    257     return;
    258   }
    259   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   GNUNET_assert (strlen (str) < TALER_CURRENCY_LEN);
    265   strcpy (cspec->currency,
    266           str);
    267   GNUNET_free (str);
    268 
    269   if (GNUNET_OK !=
    270       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
    271                                              section,
    272                                              "NAME",
    273                                              &str))
    274   {
    275     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    276                                section,
    277                                "NAME");
    278     cpc->failure = true;
    279     return;
    280   }
    281   cspec->name = str;
    282 
    283   if (GNUNET_OK !=
    284       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
    285                                              section,
    286                                              "COMMON_AMOUNTS",
    287                                              &str))
    288   {
    289     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_WARNING,
    290                                section,
    291                                "COMMON_AMOUNTS");
    292   }
    293   else
    294   {
    295     for (const char *tok = strtok (str,
    296                                    " ");
    297          NULL != tok;
    298          tok = strtok (NULL,
    299                        " "))
    300     {
    301       struct TALER_Amount val;
    302 
    303       if (GNUNET_OK !=
    304           TALER_string_to_amount (tok,
    305                                   &val))
    306       {
    307         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    308                                    section,
    309                                    "COMMON_AMOUNTS",
    310                                    tok);
    311         GNUNET_free (str);
    312         cpc->failure = true;
    313         return;
    314       }
    315       if (0 != strcasecmp (val.currency,
    316                            cspec->currency))
    317       {
    318         GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    319                                    section,
    320                                    "COMMON_AMOUNTS",
    321                                    "currency mismatch");
    322         GNUNET_free (str);
    323         cpc->failure = true;
    324         return;
    325       }
    326       GNUNET_array_append (cspec->common_amounts,
    327                            cspec->num_common_amounts,
    328                            val);
    329     }
    330     GNUNET_free (str);
    331   }
    332   if (GNUNET_OK !=
    333       GNUNET_CONFIGURATION_get_value_number (cpc->cfg,
    334                                              section,
    335                                              "FRACTIONAL_INPUT_DIGITS",
    336                                              &num))
    337   {
    338     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    339                                section,
    340                                "FRACTIONAL_INPUT_DIGITS");
    341     cpc->failure = true;
    342     return;
    343   }
    344   if (num > TALER_AMOUNT_FRAC_LEN)
    345   {
    346     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    347                                section,
    348                                "FRACTIONAL_INPUT_DIGITS",
    349                                "Number given is too big");
    350     cpc->failure = true;
    351     return;
    352   }
    353   cspec->num_fractional_input_digits = num;
    354   if (GNUNET_OK !=
    355       GNUNET_CONFIGURATION_get_value_number (cpc->cfg,
    356                                              section,
    357                                              "FRACTIONAL_NORMAL_DIGITS",
    358                                              &num))
    359   {
    360     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    361                                section,
    362                                "FRACTIONAL_NORMAL_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_NORMAL_DIGITS",
    371                                "Number given is too big");
    372     cpc->failure = true;
    373     return;
    374   }
    375   cspec->num_fractional_normal_digits = num;
    376   if (GNUNET_OK !=
    377       GNUNET_CONFIGURATION_get_value_number (cpc->cfg,
    378                                              section,
    379                                              "FRACTIONAL_TRAILING_ZERO_DIGITS",
    380                                              &num))
    381   {
    382     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    383                                section,
    384                                "FRACTIONAL_TRAILING_ZERO_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_TRAILING_ZERO_DIGITS",
    393                                "Number given is too big");
    394     cpc->failure = true;
    395     return;
    396   }
    397   cspec->num_fractional_trailing_zero_digits = num;
    398 
    399   if (GNUNET_OK !=
    400       GNUNET_CONFIGURATION_get_value_string (cpc->cfg,
    401                                              section,
    402                                              "ALT_UNIT_NAMES",
    403                                              &str))
    404   {
    405     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    406                                section,
    407                                "ALT_UNIT_NAMES");
    408     cpc->failure = true;
    409     return;
    410   }
    411   {
    412     json_error_t err;
    413 
    414     cspec->map_alt_unit_names = json_loads (str,
    415                                             JSON_REJECT_DUPLICATES,
    416                                             &err);
    417     GNUNET_free (str);
    418     if (NULL == cspec->map_alt_unit_names)
    419     {
    420       GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    421                                  section,
    422                                  "ALT_UNIT_NAMES",
    423                                  err.text);
    424       cpc->failure = true;
    425       return;
    426     }
    427   }
    428   if (GNUNET_OK !=
    429       TALER_check_currency_scale_map (cspec->map_alt_unit_names))
    430   {
    431     GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR,
    432                                section,
    433                                "ALT_UNIT_NAMES",
    434                                "invalid map entry detected");
    435     cpc->failure = true;
    436     json_decref (cspec->map_alt_unit_names);
    437     cspec->map_alt_unit_names = NULL;
    438     return;
    439   }
    440 }
    441 
    442 
    443 enum GNUNET_GenericReturnValue
    444 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   bool zf = false;
    450 
    451   if (! json_is_object (map))
    452   {
    453     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    454                 "Object required for currency scale map\n");
    455     return GNUNET_SYSERR;
    456   }
    457   json_object_foreach ((json_t *) map, str, val)
    458   {
    459     int idx;
    460     char dummy;
    461 
    462     if ( (1 != sscanf (str,
    463                        "%d%c",
    464                        &idx,
    465                        &dummy)) ||
    466          (idx < -12) ||
    467          (idx > 24) ||
    468          (! json_is_string (val) ) )
    469     {
    470       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    471                   "Invalid entry `%s' in currency scale map\n",
    472                   str);
    473       return GNUNET_SYSERR;
    474     }
    475     if (0 == idx)
    476       zf = true;
    477   }
    478   if (! zf)
    479   {
    480     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    481                 "Entry for 0 missing in currency scale map\n");
    482     return GNUNET_SYSERR;
    483   }
    484   return GNUNET_OK;
    485 }
    486 
    487 
    488 enum GNUNET_GenericReturnValue
    489 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   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   GNUNET_CONFIGURATION_iterate_sections (cfg,
    504                                          &parse_currencies_cb,
    505                                          &cpc);
    506   if (cpc.failure)
    507   {
    508     GNUNET_array_grow (cpc.cspecs,
    509                        cpc.len_cspecs,
    510                        0);
    511     return GNUNET_SYSERR;
    512   }
    513   /* Make sure that there is some sane fallback for the main currency */
    514   if (NULL != main_currency)
    515   {
    516     struct TALER_CurrencySpecification *mspec = NULL;
    517     for (unsigned int i = 0; i<cpc.num_currencies; i++)
    518     {
    519       struct TALER_CurrencySpecification *cspec;
    520 
    521       cspec = &cpc.cspecs[i];
    522       if (0 == strcmp (main_currency,
    523                        cspec->currency))
    524       {
    525         mspec = cspec;
    526         break;
    527       }
    528     }
    529     if (NULL == mspec)
    530     {
    531       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    532                   "Lacking enabled currency specification for main currency %s, using fallback currency specification.\n",
    533                   main_currency);
    534       if (cpc.len_cspecs == cpc.num_currencies)
    535       {
    536         GNUNET_array_grow (cpc.cspecs,
    537                            cpc.len_cspecs,
    538                            cpc.len_cspecs + 1);
    539       }
    540       mspec = &cpc.cspecs[cpc.num_currencies++];
    541       *mspec = defspec;
    542       GNUNET_assert (strlen (main_currency) < TALER_CURRENCY_LEN);
    543       strcpy (mspec->currency,
    544               main_currency);
    545       mspec->map_alt_unit_names
    546         = GNUNET_JSON_PACK (
    547             GNUNET_JSON_pack_string ("0",
    548                                      main_currency)
    549             );
    550       mspec->name = GNUNET_strdup (main_currency);
    551     }
    552   }
    553   /* cspecs might've been overgrown, grow back to minimum size */
    554   GNUNET_array_grow (cpc.cspecs,
    555                      cpc.len_cspecs,
    556                      cpc.num_currencies);
    557   *num_currencies = cpc.num_currencies;
    558   *cspecs = cpc.cspecs;
    559   if (0 == *num_currencies)
    560   {
    561     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    562                 "No currency formatting specification found! Please check your installation!\n");
    563     return GNUNET_NO;
    564   }
    565   return GNUNET_OK;
    566 }
    567 
    568 
    569 void
    570 TALER_CONFIG_free_currencies (
    571   unsigned int num_currencies,
    572   struct TALER_CurrencySpecification cspecs[static num_currencies])
    573 {
    574   for (unsigned int i = 0; i<num_currencies; i++)
    575   {
    576     struct TALER_CurrencySpecification *cspec = &cspecs[i];
    577 
    578     GNUNET_free (cspec->name);
    579     json_decref (cspec->map_alt_unit_names);
    580     GNUNET_array_grow (cspec->common_amounts,
    581                        cspec->num_common_amounts,
    582                        0);
    583   }
    584   GNUNET_array_grow (cspecs,
    585                      num_currencies,
    586                      0);
    587 }