merchant

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

token_family_parse.c (12436B)


      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       GNUNET_array_grow (family->details.subscription.trusted_domains,
    181                          family->details.subscription.trusted_domains_len,
    182                          json_array_size (trusted_domains));
    183 
    184       for (unsigned int i = 0;
    185            i < family->details.subscription.trusted_domains_len;
    186            i++)
    187       {
    188         const json_t *jdomain;
    189         jdomain = json_array_get (trusted_domains, i);
    190 
    191         if (! json_is_string (jdomain))
    192         {
    193           GNUNET_break_op (0);
    194           return GNUNET_SYSERR;
    195         }
    196 
    197         family->details.subscription.trusted_domains[i] =
    198           GNUNET_strdup (json_string_value (jdomain));
    199       }
    200 
    201       return GNUNET_OK;
    202     }
    203   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
    204     {
    205       const json_t *expected_domains;
    206       struct GNUNET_JSON_Specification spec[] = {
    207         GNUNET_JSON_spec_array_const ("expected_domains",
    208                                       &expected_domains),
    209         GNUNET_JSON_spec_end ()
    210       };
    211 
    212       if (GNUNET_OK !=
    213           GNUNET_JSON_parse (root,
    214                              spec,
    215                              &ename,
    216                              &eline))
    217       {
    218         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    219                     "Failed to parse %s at %u: %s\n",
    220                     spec[eline].field,
    221                     eline,
    222                     ename);
    223         GNUNET_break_op (0);
    224         return GNUNET_SYSERR;
    225       }
    226 
    227       GNUNET_array_grow (family->details.discount.expected_domains,
    228                          family->details.discount.expected_domains_len,
    229                          json_array_size (expected_domains));
    230 
    231       for (unsigned int i = 0;
    232            i < family->details.discount.expected_domains_len;
    233            i++)
    234       {
    235         const json_t *jdomain;
    236 
    237         jdomain = json_array_get (expected_domains,
    238                                   i);
    239         if (! json_is_string (jdomain))
    240         {
    241           GNUNET_break_op (0);
    242           return GNUNET_SYSERR;
    243         }
    244 
    245         family->details.discount.expected_domains[i] =
    246           GNUNET_strdup (json_string_value (jdomain));
    247       }
    248 
    249       return GNUNET_OK;
    250     }
    251   }
    252 
    253   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    254               "Field 'type' invalid in token family\n");
    255   GNUNET_break_op (0);
    256   return GNUNET_SYSERR;
    257 }
    258 
    259 
    260 /**
    261  * Provide specification to parse given JSON object to contract token details
    262  * in the contract token family. All fields from @a family are copied.
    263  *
    264  * @param name name of the token details field in the JSON
    265  * @param[out] family token_family where the token details have to be written
    266  */
    267 static struct GNUNET_JSON_Specification
    268 spec_token_details (const char *name,
    269                     struct TALER_MERCHANT_ContractTokenFamily *family)
    270 {
    271   struct GNUNET_JSON_Specification ret = {
    272     .parser = &parse_token_details,
    273     .field = name,
    274     .ptr = family,
    275   };
    276 
    277   return ret;
    278 }
    279 
    280 
    281 /**
    282  * Parse given JSON object to token families array.
    283  *
    284  * @param cls closure, pointer to array length
    285  * @param root the json object representing the token families. The keys are
    286  *             the token family slugs.
    287  * @param[out] ospec where to write the data
    288  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    289  */
    290 static enum GNUNET_GenericReturnValue
    291 parse_token_families (void *cls,
    292                       json_t *root,
    293                       struct GNUNET_JSON_Specification *ospec)
    294 {
    295   struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
    296   unsigned int *families_len = cls;
    297   json_t *jfamily;
    298   const char *slug;
    299 
    300   if (! json_is_object (root))
    301   {
    302     GNUNET_break_op (0);
    303     return GNUNET_SYSERR;
    304   }
    305 
    306   json_object_foreach (root, slug, jfamily)
    307   {
    308     const json_t *keys;
    309     struct TALER_MERCHANT_ContractTokenFamily family = {
    310       .slug = GNUNET_strdup (slug)
    311     };
    312     struct GNUNET_JSON_Specification spec[] = {
    313       GNUNET_JSON_spec_string_copy ("name",
    314                                     &family.name),
    315       GNUNET_JSON_spec_string_copy ("description",
    316                                     &family.description),
    317       GNUNET_JSON_spec_mark_optional (
    318         GNUNET_JSON_spec_object_copy ("description_i18n",
    319                                       &family.description_i18n),
    320         NULL),
    321       GNUNET_JSON_spec_array_const ("keys",
    322                                     &keys),
    323       spec_token_details ("details",
    324                           &family),
    325       GNUNET_JSON_spec_bool ("critical",
    326                              &family.critical),
    327       GNUNET_JSON_spec_end ()
    328     };
    329     const char *error_name;
    330     unsigned int error_line;
    331 
    332     if (GNUNET_OK !=
    333         GNUNET_JSON_parse (jfamily,
    334                            spec,
    335                            &error_name,
    336                            &error_line))
    337     {
    338       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    339                   "Failed to parse %s at %u: %s\n",
    340                   spec[error_line].field,
    341                   error_line,
    342                   error_name);
    343       GNUNET_break_op (0);
    344       TALER_MERCHANT_contract_token_family_free (&family);
    345       return GNUNET_SYSERR;
    346     }
    347 
    348     GNUNET_array_grow (family.keys,
    349                        family.keys_len,
    350                        json_array_size (keys));
    351 
    352     for (unsigned int i = 0; i<family.keys_len; i++)
    353     {
    354       struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
    355       struct GNUNET_JSON_Specification key_spec[] = {
    356         TALER_JSON_spec_token_pub (
    357           NULL,
    358           &key->pub),
    359         GNUNET_JSON_spec_timestamp (
    360           "signature_validity_start",
    361           &key->valid_after),
    362         GNUNET_JSON_spec_timestamp (
    363           "signature_validity_end",
    364           &key->valid_before),
    365         GNUNET_JSON_spec_end ()
    366       };
    367       const char *ierror_name;
    368       unsigned int ierror_line;
    369 
    370       if (GNUNET_OK !=
    371           GNUNET_JSON_parse (json_array_get (keys,
    372                                              i),
    373                              key_spec,
    374                              &ierror_name,
    375                              &ierror_line))
    376       {
    377         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    378                     "Failed to parse %s at %u: %s\n",
    379                     key_spec[ierror_line].field,
    380                     ierror_line,
    381                     ierror_name);
    382         GNUNET_break_op (0);
    383         TALER_MERCHANT_contract_token_family_free (&family);
    384         return GNUNET_SYSERR;
    385       }
    386     }
    387 
    388     GNUNET_array_append (*families,
    389                          *families_len,
    390                          family);
    391   }
    392 
    393   return GNUNET_OK;
    394 }
    395 
    396 
    397 struct GNUNET_JSON_Specification
    398 TALER_MERCHANT_spec_token_families (
    399   const char *name,
    400   struct TALER_MERCHANT_ContractTokenFamily **families,
    401   unsigned int *families_len)
    402 {
    403   struct GNUNET_JSON_Specification ret = {
    404     .cls = (void *) families_len,
    405     .parser = &parse_token_families,
    406     .field = name,
    407     .ptr = families,
    408   };
    409 
    410   return ret;
    411 }