merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

token_family_parse.c (13160B)


      1 /*
      2   This file is part of TALER
      3   (C) 2026 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser 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 src/util/token_family_parse.c
     18  * @brief parser for data about token families
     19  * @author Christian Grothoff
     20  */
     21 #include "platform.h"
     22 #include <string.h>
     23 #include "taler/taler_merchant_util.h"
     24 #include <taler/taler_json_lib.h>
     25 #include <gnunet/gnunet_json_lib.h>
     26 
     27 
     28 void
     29 TALER_MERCHANT_contract_token_family_free (
     30   struct TALER_MERCHANT_ContractTokenFamily *family)
     31 {
     32   GNUNET_free (family->slug);
     33   GNUNET_free (family->name);
     34   GNUNET_free (family->description);
     35   if (NULL != family->description_i18n)
     36   {
     37     json_decref (family->description_i18n);
     38     family->description_i18n = NULL;
     39   }
     40   for (unsigned int i = 0; i < family->keys_len; i++)
     41     TALER_token_issue_pub_free (&family->keys[i].pub);
     42   GNUNET_array_grow (family->keys,
     43                      family->keys_len,
     44                      0);
     45   switch (family->kind)
     46   {
     47   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     48     GNUNET_break (0);
     49     break;
     50   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     51     for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
     52          i++)
     53       GNUNET_free (family->details.discount.expected_domains[i]);
     54     GNUNET_array_grow (family->details.discount.expected_domains,
     55                        family->details.discount.expected_domains_len,
     56                        0);
     57     break;
     58   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     59     for (unsigned int i = 0; i < family->details.subscription.
     60          trusted_domains_len; i++)
     61       GNUNET_free (family->details.subscription.trusted_domains[i]);
     62     GNUNET_array_grow (family->details.subscription.trusted_domains,
     63                        family->details.subscription.trusted_domains_len,
     64                        0);
     65     break;
     66   }
     67 }
     68 
     69 
     70 enum GNUNET_GenericReturnValue
     71 TALER_MERCHANT_find_token_family_key (
     72   const char *slug,
     73   struct GNUNET_TIME_Timestamp valid_after,
     74   const struct TALER_MERCHANT_ContractTokenFamily *families,
     75   unsigned int families_len,
     76   struct TALER_MERCHANT_ContractTokenFamily *family,
     77   struct TALER_MERCHANT_ContractTokenFamilyKey *key)
     78 {
     79   for (unsigned int i = 0; i < families_len; i++)
     80   {
     81     const struct TALER_MERCHANT_ContractTokenFamily *fami
     82       = &families[i];
     83 
     84     if (0 != strcmp (fami->slug,
     85                      slug))
     86       continue;
     87     if (NULL != family)
     88       *family = *fami;
     89     for (unsigned int k = 0; k < fami->keys_len; k++)
     90     {
     91       struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
     92 
     93       if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
     94                                      ==,
     95                                      valid_after))
     96       {
     97         if (NULL != key)
     98           *key = *ki;
     99         return GNUNET_OK;
    100       }
    101     }
    102     /* matching family found, but no key. */
    103     return GNUNET_NO;
    104   }
    105 
    106   /* no matching family found */
    107   return GNUNET_SYSERR;
    108 }
    109 
    110 
    111 /**
    112  * Parse token details of the given JSON contract token family.
    113  *
    114  * @param cls closure, unused parameter
    115  * @param root the JSON object representing data
    116  * @param[out] ospec where to write the data
    117  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    118  */
    119 static enum GNUNET_GenericReturnValue
    120 parse_token_details (void *cls,
    121                      json_t *root,
    122                      struct GNUNET_JSON_Specification *ospec)
    123 {
    124   struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
    125   const char *class;
    126   const char *ename;
    127   unsigned int eline;
    128   struct GNUNET_JSON_Specification ispec[] = {
    129     GNUNET_JSON_spec_string ("class",
    130                              &class),
    131     GNUNET_JSON_spec_end ()
    132   };
    133 
    134   (void) cls;
    135   if (GNUNET_OK !=
    136       GNUNET_JSON_parse (root,
    137                          ispec,
    138                          &ename,
    139                          &eline))
    140   {
    141     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    142                 "Failed to parse %s at %u: %s\n",
    143                 ispec[eline].field,
    144                 eline,
    145                 ename);
    146     GNUNET_break_op (0);
    147     return GNUNET_SYSERR;
    148   }
    149 
    150   family->kind = TALER_MERCHANT_contract_token_kind_from_string (class);
    151 
    152   switch (family->kind)
    153   {
    154   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
    155     break;
    156   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
    157     {
    158       const json_t *trusted_domains;
    159       struct GNUNET_JSON_Specification spec[] = {
    160         GNUNET_JSON_spec_array_const ("trusted_domains",
    161                                       &trusted_domains),
    162         GNUNET_JSON_spec_end ()
    163       };
    164 
    165       if (GNUNET_OK !=
    166           GNUNET_JSON_parse (root,
    167                              spec,
    168                              &ename,
    169                              &eline))
    170       {
    171         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    172                     "Failed to parse %s at %u: %s\n",
    173                     spec[eline].field,
    174                     eline,
    175                     ename);
    176         GNUNET_break_op (0);
    177         return GNUNET_SYSERR;
    178       }
    179 
    180       /* Check the size_t count before allocation or conversion to the
    181          unsigned array length. A remote response must not reach the
    182          allocator's fatal size limit. */
    183       if (json_array_size (trusted_domains) >
    184           TALER_MERCHANT_MAX_TOKEN_FAMILY_DOMAINS)
    185       {
    186         GNUNET_break_op (0);
    187         return GNUNET_SYSERR;
    188       }
    189       GNUNET_array_grow (family->details.subscription.trusted_domains,
    190                          family->details.subscription.trusted_domains_len,
    191                          json_array_size (trusted_domains));
    192 
    193       for (unsigned int i = 0;
    194            i < family->details.subscription.trusted_domains_len;
    195            i++)
    196       {
    197         const json_t *jdomain;
    198         jdomain = json_array_get (trusted_domains, i);
    199 
    200         if (! json_is_string (jdomain))
    201         {
    202           GNUNET_break_op (0);
    203           return GNUNET_SYSERR;
    204         }
    205 
    206         family->details.subscription.trusted_domains[i] =
    207           GNUNET_strdup (json_string_value (jdomain));
    208       }
    209 
    210       return GNUNET_OK;
    211     }
    212   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
    213     {
    214       const json_t *expected_domains;
    215       struct GNUNET_JSON_Specification spec[] = {
    216         GNUNET_JSON_spec_array_const ("expected_domains",
    217                                       &expected_domains),
    218         GNUNET_JSON_spec_end ()
    219       };
    220 
    221       if (GNUNET_OK !=
    222           GNUNET_JSON_parse (root,
    223                              spec,
    224                              &ename,
    225                              &eline))
    226       {
    227         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    228                     "Failed to parse %s at %u: %s\n",
    229                     spec[eline].field,
    230                     eline,
    231                     ename);
    232         GNUNET_break_op (0);
    233         return GNUNET_SYSERR;
    234       }
    235 
    236       if (json_array_size (expected_domains) >
    237           TALER_MERCHANT_MAX_TOKEN_FAMILY_DOMAINS)
    238       {
    239         GNUNET_break_op (0);
    240         return GNUNET_SYSERR;
    241       }
    242       GNUNET_array_grow (family->details.discount.expected_domains,
    243                          family->details.discount.expected_domains_len,
    244                          json_array_size (expected_domains));
    245 
    246       for (unsigned int i = 0;
    247            i < family->details.discount.expected_domains_len;
    248            i++)
    249       {
    250         const json_t *jdomain;
    251 
    252         jdomain = json_array_get (expected_domains,
    253                                   i);
    254         if (! json_is_string (jdomain))
    255         {
    256           GNUNET_break_op (0);
    257           return GNUNET_SYSERR;
    258         }
    259 
    260         family->details.discount.expected_domains[i] =
    261           GNUNET_strdup (json_string_value (jdomain));
    262       }
    263 
    264       return GNUNET_OK;
    265     }
    266   }
    267 
    268   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    269               "Field 'type' invalid in token family\n");
    270   GNUNET_break_op (0);
    271   return GNUNET_SYSERR;
    272 }
    273 
    274 
    275 /**
    276  * Provide specification to parse given JSON object to contract token details
    277  * in the contract token family. All fields from @a family are copied.
    278  *
    279  * @param name name of the token details field in the JSON
    280  * @param[out] family token_family where the token details have to be written
    281  */
    282 static struct GNUNET_JSON_Specification
    283 spec_token_details (const char *name,
    284                     struct TALER_MERCHANT_ContractTokenFamily *family)
    285 {
    286   struct GNUNET_JSON_Specification ret = {
    287     .parser = &parse_token_details,
    288     .field = name,
    289     .ptr = family,
    290   };
    291 
    292   return ret;
    293 }
    294 
    295 
    296 /**
    297  * Parse given JSON object to token families array.
    298  *
    299  * @param cls closure, pointer to array length
    300  * @param root the json object representing the token families. The keys are
    301  *             the token family slugs.
    302  * @param[out] ospec where to write the data
    303  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    304  */
    305 static enum GNUNET_GenericReturnValue
    306 parse_token_families (void *cls,
    307                       json_t *root,
    308                       struct GNUNET_JSON_Specification *ospec)
    309 {
    310   struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
    311   unsigned int *families_len = cls;
    312   json_t *jfamily;
    313   const char *slug;
    314 
    315   if (! json_is_object (root))
    316   {
    317     GNUNET_break_op (0);
    318     return GNUNET_SYSERR;
    319   }
    320 
    321   json_object_foreach (root, slug, jfamily)
    322   {
    323     const json_t *keys;
    324     struct TALER_MERCHANT_ContractTokenFamily family = {
    325       .slug = GNUNET_strdup (slug)
    326     };
    327     struct GNUNET_JSON_Specification spec[] = {
    328       GNUNET_JSON_spec_string_copy ("name",
    329                                     &family.name),
    330       GNUNET_JSON_spec_string_copy ("description",
    331                                     &family.description),
    332       GNUNET_JSON_spec_mark_optional (
    333         GNUNET_JSON_spec_object_copy ("description_i18n",
    334                                       &family.description_i18n),
    335         NULL),
    336       GNUNET_JSON_spec_array_const ("keys",
    337                                     &keys),
    338       spec_token_details ("details",
    339                           &family),
    340       GNUNET_JSON_spec_bool ("critical",
    341                              &family.critical),
    342       GNUNET_JSON_spec_end ()
    343     };
    344     const char *error_name;
    345     unsigned int error_line;
    346 
    347     if (GNUNET_OK !=
    348         GNUNET_JSON_parse (jfamily,
    349                            spec,
    350                            &error_name,
    351                            &error_line))
    352     {
    353       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    354                   "Failed to parse %s at %u: %s\n",
    355                   spec[error_line].field,
    356                   error_line,
    357                   error_name);
    358       GNUNET_break_op (0);
    359       TALER_MERCHANT_contract_token_family_free (&family);
    360       return GNUNET_SYSERR;
    361     }
    362 
    363     if (json_array_size (keys) > TALER_MERCHANT_MAX_TOKEN_FAMILY_KEYS)
    364     {
    365       GNUNET_break_op (0);
    366       TALER_MERCHANT_contract_token_family_free (&family);
    367       return GNUNET_SYSERR;
    368     }
    369     GNUNET_array_grow (family.keys,
    370                        family.keys_len,
    371                        json_array_size (keys));
    372 
    373     for (unsigned int i = 0; i<family.keys_len; i++)
    374     {
    375       struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
    376       struct GNUNET_JSON_Specification key_spec[] = {
    377         TALER_JSON_spec_token_pub (
    378           NULL,
    379           &key->pub),
    380         GNUNET_JSON_spec_timestamp (
    381           "signature_validity_start",
    382           &key->valid_after),
    383         GNUNET_JSON_spec_timestamp (
    384           "signature_validity_end",
    385           &key->valid_before),
    386         GNUNET_JSON_spec_end ()
    387       };
    388       const char *ierror_name;
    389       unsigned int ierror_line;
    390 
    391       if (GNUNET_OK !=
    392           GNUNET_JSON_parse (json_array_get (keys,
    393                                              i),
    394                              key_spec,
    395                              &ierror_name,
    396                              &ierror_line))
    397       {
    398         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    399                     "Failed to parse %s at %u: %s\n",
    400                     key_spec[ierror_line].field,
    401                     ierror_line,
    402                     ierror_name);
    403         GNUNET_break_op (0);
    404         TALER_MERCHANT_contract_token_family_free (&family);
    405         return GNUNET_SYSERR;
    406       }
    407     }
    408 
    409     GNUNET_array_append (*families,
    410                          *families_len,
    411                          family);
    412   }
    413 
    414   return GNUNET_OK;
    415 }
    416 
    417 
    418 struct GNUNET_JSON_Specification
    419 TALER_MERCHANT_spec_token_families (
    420   const char *name,
    421   struct TALER_MERCHANT_ContractTokenFamily **families,
    422   unsigned int *families_len)
    423 {
    424   struct GNUNET_JSON_Specification ret = {
    425     .cls = (void *) families_len,
    426     .parser = &parse_token_families,
    427     .field = name,
    428     .ptr = families,
    429   };
    430 
    431   return ret;
    432 }