LCOV - code coverage report
Current view: top level - util - token_family_parse.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 76.4 % 140 107
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 6 6

            Line data    Source code
       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           20 : TALER_MERCHANT_contract_token_family_free (
      30              :   struct TALER_MERCHANT_ContractTokenFamily *family)
      31              : {
      32           20 :   GNUNET_free (family->slug);
      33           20 :   GNUNET_free (family->name);
      34           20 :   GNUNET_free (family->description);
      35           20 :   if (NULL != family->description_i18n)
      36              :   {
      37           20 :     json_decref (family->description_i18n);
      38           20 :     family->description_i18n = NULL;
      39              :   }
      40           38 :   for (unsigned int i = 0; i < family->keys_len; i++)
      41           18 :     TALER_token_issue_pub_free (&family->keys[i].pub);
      42           20 :   GNUNET_array_grow (family->keys,
      43              :                      family->keys_len,
      44              :                      0);
      45           20 :   switch (family->kind)
      46              :   {
      47            0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
      48            0 :     GNUNET_break (0);
      49            0 :     break;
      50            4 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
      51           10 :     for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
      52            6 :          i++)
      53            6 :       GNUNET_free (family->details.discount.expected_domains[i]);
      54            4 :     GNUNET_array_grow (family->details.discount.expected_domains,
      55              :                        family->details.discount.expected_domains_len,
      56              :                        0);
      57            4 :     break;
      58           16 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
      59           22 :     for (unsigned int i = 0; i < family->details.subscription.
      60            6 :          trusted_domains_len; i++)
      61            6 :       GNUNET_free (family->details.subscription.trusted_domains[i]);
      62           16 :     GNUNET_array_grow (family->details.subscription.trusted_domains,
      63              :                        family->details.subscription.trusted_domains_len,
      64              :                        0);
      65           16 :     break;
      66              :   }
      67           20 : }
      68              : 
      69              : 
      70              : enum GNUNET_GenericReturnValue
      71            2 : 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            3 :   for (unsigned int i = 0; i < families_len; i++)
      80              :   {
      81            3 :     const struct TALER_MERCHANT_ContractTokenFamily *fami
      82            3 :       = &families[i];
      83              : 
      84            3 :     if (0 != strcmp (fami->slug,
      85              :                      slug))
      86            1 :       continue;
      87            2 :     if (NULL != family)
      88            2 :       *family = *fami;
      89            2 :     for (unsigned int k = 0; k < fami->keys_len; k++)
      90              :     {
      91            2 :       struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
      92              : 
      93            2 :       if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
      94              :                                      ==,
      95              :                                      valid_after))
      96              :       {
      97            2 :         if (NULL != key)
      98            2 :           *key = *ki;
      99            2 :         return GNUNET_OK;
     100              :       }
     101              :     }
     102              :     /* matching family found, but no key. */
     103            0 :     return GNUNET_NO;
     104              :   }
     105              : 
     106              :   /* no matching family found */
     107            0 :   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           11 : parse_token_details (void *cls,
     121              :                      json_t *root,
     122              :                      struct GNUNET_JSON_Specification *ospec)
     123              : {
     124           11 :   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           11 :     GNUNET_JSON_spec_string ("class",
     130              :                              &class),
     131           11 :     GNUNET_JSON_spec_end ()
     132              :   };
     133              : 
     134              :   (void) cls;
     135           11 :   if (GNUNET_OK !=
     136           11 :       GNUNET_JSON_parse (root,
     137              :                          ispec,
     138              :                          &ename,
     139              :                          &eline))
     140              :   {
     141            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     142              :                 "Failed to parse %s at %u: %s\n",
     143              :                 ispec[eline].field,
     144              :                 eline,
     145              :                 ename);
     146            0 :     GNUNET_break_op (0);
     147            0 :     return GNUNET_SYSERR;
     148              :   }
     149              : 
     150           11 :   family->kind = TALER_MERCHANT_contract_token_kind_from_string (class);
     151              : 
     152           11 :   switch (family->kind)
     153              :   {
     154            0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     155            0 :     break;
     156            8 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     157              :     {
     158              :       const json_t *trusted_domains;
     159              :       struct GNUNET_JSON_Specification spec[] = {
     160            8 :         GNUNET_JSON_spec_array_const ("trusted_domains",
     161              :                                       &trusted_domains),
     162            8 :         GNUNET_JSON_spec_end ()
     163              :       };
     164              : 
     165            8 :       if (GNUNET_OK !=
     166            8 :           GNUNET_JSON_parse (root,
     167              :                              spec,
     168              :                              &ename,
     169              :                              &eline))
     170              :       {
     171            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     172              :                     "Failed to parse %s at %u: %s\n",
     173              :                     spec[eline].field,
     174              :                     eline,
     175              :                     ename);
     176            0 :         GNUNET_break_op (0);
     177            0 :         return GNUNET_SYSERR;
     178              :       }
     179              : 
     180            8 :       GNUNET_array_grow (family->details.subscription.trusted_domains,
     181              :                          family->details.subscription.trusted_domains_len,
     182              :                          json_array_size (trusted_domains));
     183              : 
     184            8 :       for (unsigned int i = 0;
     185           14 :            i < family->details.subscription.trusted_domains_len;
     186            6 :            i++)
     187              :       {
     188              :         const json_t *jdomain;
     189            6 :         jdomain = json_array_get (trusted_domains, i);
     190              : 
     191            6 :         if (! json_is_string (jdomain))
     192              :         {
     193            0 :           GNUNET_break_op (0);
     194            0 :           return GNUNET_SYSERR;
     195              :         }
     196              : 
     197            6 :         family->details.subscription.trusted_domains[i] =
     198            6 :           GNUNET_strdup (json_string_value (jdomain));
     199              :       }
     200              : 
     201            8 :       return GNUNET_OK;
     202              :     }
     203            3 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     204              :     {
     205              :       const json_t *expected_domains;
     206              :       struct GNUNET_JSON_Specification spec[] = {
     207            3 :         GNUNET_JSON_spec_array_const ("expected_domains",
     208              :                                       &expected_domains),
     209            3 :         GNUNET_JSON_spec_end ()
     210              :       };
     211              : 
     212            3 :       if (GNUNET_OK !=
     213            3 :           GNUNET_JSON_parse (root,
     214              :                              spec,
     215              :                              &ename,
     216              :                              &eline))
     217              :       {
     218            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     219              :                     "Failed to parse %s at %u: %s\n",
     220              :                     spec[eline].field,
     221              :                     eline,
     222              :                     ename);
     223            0 :         GNUNET_break_op (0);
     224            0 :         return GNUNET_SYSERR;
     225              :       }
     226              : 
     227            3 :       GNUNET_array_grow (family->details.discount.expected_domains,
     228              :                          family->details.discount.expected_domains_len,
     229              :                          json_array_size (expected_domains));
     230              : 
     231            3 :       for (unsigned int i = 0;
     232            9 :            i < family->details.discount.expected_domains_len;
     233            6 :            i++)
     234              :       {
     235              :         const json_t *jdomain;
     236              : 
     237            6 :         jdomain = json_array_get (expected_domains,
     238              :                                   i);
     239            6 :         if (! json_is_string (jdomain))
     240              :         {
     241            0 :           GNUNET_break_op (0);
     242            0 :           return GNUNET_SYSERR;
     243              :         }
     244              : 
     245            6 :         family->details.discount.expected_domains[i] =
     246            6 :           GNUNET_strdup (json_string_value (jdomain));
     247              :       }
     248              : 
     249            3 :       return GNUNET_OK;
     250              :     }
     251              :   }
     252              : 
     253            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     254              :               "Field 'type' invalid in token family\n");
     255            0 :   GNUNET_break_op (0);
     256            0 :   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           11 : spec_token_details (const char *name,
     269              :                     struct TALER_MERCHANT_ContractTokenFamily *family)
     270              : {
     271           11 :   struct GNUNET_JSON_Specification ret = {
     272              :     .parser = &parse_token_details,
     273              :     .field = name,
     274              :     .ptr = family,
     275              :   };
     276              : 
     277           11 :   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           27 : parse_token_families (void *cls,
     292              :                       json_t *root,
     293              :                       struct GNUNET_JSON_Specification *ospec)
     294              : {
     295           27 :   struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
     296           27 :   unsigned int *families_len = cls;
     297              :   json_t *jfamily;
     298              :   const char *slug;
     299              : 
     300           27 :   if (! json_is_object (root))
     301              :   {
     302            0 :     GNUNET_break_op (0);
     303            0 :     return GNUNET_SYSERR;
     304              :   }
     305              : 
     306           38 :   json_object_foreach (root, slug, jfamily)
     307              :   {
     308              :     const json_t *keys;
     309           22 :     struct TALER_MERCHANT_ContractTokenFamily family = {
     310           11 :       .slug = GNUNET_strdup (slug)
     311              :     };
     312              :     struct GNUNET_JSON_Specification spec[] = {
     313           11 :       GNUNET_JSON_spec_string_copy ("name",
     314              :                                     &family.name),
     315           11 :       GNUNET_JSON_spec_string_copy ("description",
     316              :                                     &family.description),
     317           11 :       GNUNET_JSON_spec_mark_optional (
     318              :         GNUNET_JSON_spec_object_copy ("description_i18n",
     319              :                                       &family.description_i18n),
     320              :         NULL),
     321           11 :       GNUNET_JSON_spec_array_const ("keys",
     322              :                                     &keys),
     323           11 :       spec_token_details ("details",
     324              :                           &family),
     325           11 :       GNUNET_JSON_spec_bool ("critical",
     326              :                              &family.critical),
     327           11 :       GNUNET_JSON_spec_end ()
     328              :     };
     329              :     const char *error_name;
     330              :     unsigned int error_line;
     331              : 
     332           11 :     if (GNUNET_OK !=
     333           11 :         GNUNET_JSON_parse (jfamily,
     334              :                            spec,
     335              :                            &error_name,
     336              :                            &error_line))
     337              :     {
     338            0 :       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            0 :       GNUNET_break_op (0);
     344            0 :       TALER_MERCHANT_contract_token_family_free (&family);
     345            0 :       return GNUNET_SYSERR;
     346              :     }
     347              : 
     348           11 :     GNUNET_array_grow (family.keys,
     349              :                        family.keys_len,
     350              :                        json_array_size (keys));
     351              : 
     352           21 :     for (unsigned int i = 0; i<family.keys_len; i++)
     353              :     {
     354           10 :       struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
     355              :       struct GNUNET_JSON_Specification key_spec[] = {
     356           10 :         TALER_JSON_spec_token_pub (
     357              :           NULL,
     358              :           &key->pub),
     359           10 :         GNUNET_JSON_spec_timestamp (
     360              :           "signature_validity_start",
     361              :           &key->valid_after),
     362           10 :         GNUNET_JSON_spec_timestamp (
     363              :           "signature_validity_end",
     364              :           &key->valid_before),
     365           10 :         GNUNET_JSON_spec_end ()
     366              :       };
     367              :       const char *ierror_name;
     368              :       unsigned int ierror_line;
     369              : 
     370           10 :       if (GNUNET_OK !=
     371           10 :           GNUNET_JSON_parse (json_array_get (keys,
     372              :                                              i),
     373              :                              key_spec,
     374              :                              &ierror_name,
     375              :                              &ierror_line))
     376              :       {
     377            0 :         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            0 :         GNUNET_break_op (0);
     383            0 :         TALER_MERCHANT_contract_token_family_free (&family);
     384            0 :         return GNUNET_SYSERR;
     385              :       }
     386              :     }
     387              : 
     388           11 :     GNUNET_array_append (*families,
     389              :                          *families_len,
     390              :                          family);
     391              :   }
     392              : 
     393           27 :   return GNUNET_OK;
     394              : }
     395              : 
     396              : 
     397              : struct GNUNET_JSON_Specification
     398           27 : TALER_MERCHANT_spec_token_families (
     399              :   const char *name,
     400              :   struct TALER_MERCHANT_ContractTokenFamily **families,
     401              :   unsigned int *families_len)
     402              : {
     403           27 :   struct GNUNET_JSON_Specification ret = {
     404              :     .cls = (void *) families_len,
     405              :     .parser = &parse_token_families,
     406              :     .field = name,
     407              :     .ptr = families,
     408              :   };
     409              : 
     410           27 :   return ret;
     411              : }
        

Generated by: LCOV version 2.0-1