LCOV - code coverage report
Current view: top level - util - contract_parse.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.6 % 606 458
Test Date: 2026-01-01 16:44:56 Functions: 100.0 % 24 24

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2024, 2025 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 util/contract_parse.c
      18              :  * @brief shared logic for contract terms parsing
      19              :  * @author Iván Ávalos
      20              :  * @author Christian Grothoff
      21              :  */
      22              : #include "platform.h"
      23              : #include <gnunet/gnunet_common.h>
      24              : #include <gnunet/gnunet_json_lib.h>
      25              : #include <jansson.h>
      26              : #include <stdbool.h>
      27              : #include <stdint.h>
      28              : #include <taler/taler_json_lib.h>
      29              : #include <taler/taler_util.h>
      30              : #include "taler_merchant_util.h"
      31              : 
      32              : 
      33              : /**
      34              :  * Parse merchant details of given JSON contract terms.
      35              :  *
      36              :  * @param cls closure, unused parameter
      37              :  * @param root the JSON object representing data
      38              :  * @param[out] ospec where to write the data
      39              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
      40              :  */
      41              : static enum GNUNET_GenericReturnValue
      42          165 : parse_merchant_details (void *cls,
      43              :                         json_t *root,
      44              :                         struct GNUNET_JSON_Specification *ospec)
      45              : {
      46          165 :   struct TALER_MERCHANT_Contract *contract = ospec->ptr;
      47              :   struct GNUNET_JSON_Specification spec[] = {
      48          165 :     GNUNET_JSON_spec_string_copy ("name",
      49              :                                   &contract->merchant.name),
      50          165 :     GNUNET_JSON_spec_mark_optional (
      51              :       GNUNET_JSON_spec_string_copy ("email",
      52              :                                     &contract->merchant.email),
      53              :       NULL),
      54          165 :     GNUNET_JSON_spec_mark_optional (
      55              :       GNUNET_JSON_spec_string_copy ("website",
      56              :                                     &contract->merchant.website),
      57              :       NULL),
      58          165 :     GNUNET_JSON_spec_mark_optional (
      59              :       GNUNET_JSON_spec_string_copy ("logo",
      60              :                                     &contract->merchant.logo),
      61              :       NULL),
      62          165 :     GNUNET_JSON_spec_mark_optional (
      63              :       GNUNET_JSON_spec_object_copy ("address",
      64              :                                     &contract->merchant.address),
      65              :       NULL),
      66          165 :     GNUNET_JSON_spec_mark_optional (
      67              :       GNUNET_JSON_spec_object_copy ("jurisdiction",
      68              :                                     &contract->merchant.jurisdiction),
      69              :       NULL),
      70          165 :     GNUNET_JSON_spec_end ()
      71              :   };
      72              :   const char *error_name;
      73              :   unsigned int error_line;
      74              : 
      75              :   (void) cls;
      76          165 :   if (GNUNET_OK !=
      77          165 :       GNUNET_JSON_parse (root,
      78              :                          spec,
      79              :                          &error_name,
      80              :                          &error_line))
      81              :   {
      82            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      83              :                 "Failed to parse %s at %u: %s\n",
      84              :                 spec[error_line].field,
      85              :                 error_line,
      86              :                 error_name);
      87            0 :     GNUNET_break_op (0);
      88            0 :     return GNUNET_SYSERR;
      89              :   }
      90          165 :   return GNUNET_OK;
      91              : }
      92              : 
      93              : 
      94              : /**
      95              :  * Provide specification to parse given JSON object to merchant details in the
      96              :  * contract terms. All fields from @a contract are copied.
      97              :  *
      98              :  * @param name name of the merchant details field in the JSON
      99              :  * @param[out] contract where the merchant details have to be written
     100              :  */
     101              : static struct GNUNET_JSON_Specification
     102          165 : spec_merchant_details (const char *name,
     103              :                        struct TALER_MERCHANT_Contract *contract)
     104              : {
     105          165 :   struct GNUNET_JSON_Specification ret = {
     106              :     .parser = &parse_merchant_details,
     107              :     .field = name,
     108              :     .ptr = contract,
     109              :   };
     110              : 
     111          165 :   return ret;
     112              : }
     113              : 
     114              : 
     115              : /**
     116              :  * Get enum value from contract token type string.
     117              :  *
     118              :  * @param str contract token type string
     119              :  * @return enum value of token type
     120              :  */
     121              : static enum TALER_MERCHANT_ContractTokenKind
     122            9 : contract_token_kind_from_string (const char *str)
     123              : {
     124            9 :   if (0 == strcmp ("subscription",
     125              :                    str))
     126            7 :     return TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION;
     127            2 :   if (0 == strcmp ("discount",
     128              :                    str))
     129            2 :     return TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT;
     130            0 :   return TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID;
     131              : }
     132              : 
     133              : 
     134              : enum GNUNET_GenericReturnValue
     135           15 : TALER_MERCHANT_parse_choice_input (
     136              :   json_t *root,
     137              :   struct TALER_MERCHANT_ContractInput *input,
     138              :   size_t index,
     139              :   bool order)
     140              : {
     141              :   const char *ename;
     142              :   unsigned int eline;
     143              :   struct GNUNET_JSON_Specification ispec[] = {
     144           15 :     TALER_MERCHANT_json_spec_cit ("type",
     145              :                                   &input->type),
     146           15 :     GNUNET_JSON_spec_end ()
     147              :   };
     148              : 
     149           15 :   if (GNUNET_OK !=
     150           15 :       GNUNET_JSON_parse (root,
     151              :                          ispec,
     152              :                          &ename,
     153              :                          &eline))
     154              :   {
     155            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     156              :                 "Failed to parse %s at %u: %s\n",
     157              :                 ispec[eline].field,
     158              :                 eline,
     159              :                 ename);
     160            0 :     GNUNET_break_op (0);
     161            0 :     return GNUNET_SYSERR;
     162              :   }
     163              : 
     164           15 :   switch (input->type)
     165              :   {
     166            0 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
     167            0 :     GNUNET_break (0);
     168            0 :     break;
     169           15 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
     170              :     {
     171              :       struct GNUNET_JSON_Specification spec[] = {
     172           15 :         GNUNET_JSON_spec_string ("token_family_slug",
     173              :                                  &input->details.token.token_family_slug),
     174           15 :         GNUNET_JSON_spec_mark_optional (
     175              :           GNUNET_JSON_spec_uint32 ("count",
     176           15 :                                    &input->details.token.count),
     177              :           NULL),
     178           15 :         GNUNET_JSON_spec_end ()
     179              :       };
     180              : 
     181           15 :       if (GNUNET_OK !=
     182           15 :           GNUNET_JSON_parse (root,
     183              :                              spec,
     184              :                              &ename,
     185              :                              &eline))
     186              :       {
     187            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     188              :                     "Failed to parse %s at %u: %s\n",
     189              :                     spec[eline].field,
     190              :                     eline,
     191              :                     ename);
     192            0 :         GNUNET_break_op (0);
     193            0 :         return GNUNET_SYSERR;
     194              :       }
     195              : 
     196           15 :       return GNUNET_OK;
     197              :     }
     198              :   }
     199              : 
     200            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     201              :               "Field 'type' invalid in input #%u\n",
     202              :               (unsigned int) index);
     203            0 :   GNUNET_break_op (0);
     204            0 :   return GNUNET_SYSERR;
     205              : }
     206              : 
     207              : 
     208              : enum GNUNET_GenericReturnValue
     209           16 : TALER_MERCHANT_parse_choice_output (
     210              :   json_t *root,
     211              :   struct TALER_MERCHANT_ContractOutput *output,
     212              :   size_t index,
     213              :   bool order)
     214              : {
     215              :   const char *ename;
     216              :   unsigned int eline;
     217              :   struct GNUNET_JSON_Specification ispec[] = {
     218           16 :     TALER_MERCHANT_json_spec_cot ("type",
     219              :                                   &output->type),
     220           16 :     GNUNET_JSON_spec_end ()
     221              :   };
     222              : 
     223           16 :   if (GNUNET_OK !=
     224           16 :       GNUNET_JSON_parse (root,
     225              :                          ispec,
     226              :                          &ename,
     227              :                          &eline))
     228              :   {
     229            0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     230              :                 "Failed to parse %s at %u: %s\n",
     231              :                 ispec[eline].field,
     232              :                 eline,
     233              :                 ename);
     234            0 :     GNUNET_break_op (0);
     235            0 :     return GNUNET_SYSERR;
     236              :   }
     237              : 
     238           16 :   switch (output->type)
     239              :   {
     240            0 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     241            0 :     GNUNET_break (0);
     242            0 :     break;
     243           15 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     244              :     {
     245              :       struct GNUNET_JSON_Specification spec[] = {
     246           15 :         GNUNET_JSON_spec_string ("token_family_slug",
     247              :                                  &output->details.token.token_family_slug),
     248           15 :         GNUNET_JSON_spec_mark_optional (
     249              :           GNUNET_JSON_spec_uint ("count",
     250              :                                  &output->details.token.count),
     251              :           NULL),
     252           15 :         GNUNET_JSON_spec_mark_optional (
     253              :           GNUNET_JSON_spec_timestamp ("valid_at",
     254              :                                       &output->details.token.valid_at),
     255              :           NULL),
     256           15 :         (! order)
     257            7 :         ? GNUNET_JSON_spec_uint ("key_index",
     258              :                                  &output->details.token.key_index)
     259           15 :         : GNUNET_JSON_spec_end (),
     260           15 :         GNUNET_JSON_spec_end ()
     261              :       };
     262              : 
     263           15 :       if (GNUNET_OK !=
     264           15 :           GNUNET_JSON_parse (root,
     265              :                              spec,
     266              :                              &ename,
     267              :                              &eline))
     268              :       {
     269            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     270              :                     "Failed to parse %s at %u: %s\n",
     271              :                     spec[eline].field,
     272              :                     eline,
     273              :                     ename);
     274            0 :         GNUNET_break_op (0);
     275            0 :         return GNUNET_SYSERR;
     276              :       }
     277              : 
     278           15 :       return GNUNET_OK;
     279              :     }
     280            1 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     281              :     {
     282            1 :       const json_t *donau_urls = NULL;
     283              :       struct GNUNET_JSON_Specification spec[] = {
     284            1 :         GNUNET_JSON_spec_mark_optional (
     285              :           TALER_JSON_spec_amount_any ("amount",
     286              :                                       &output->details.donation_receipt.amount),
     287              :           NULL),
     288            1 :         (! order)
     289            1 :         ? GNUNET_JSON_spec_array_const ("donau_urls",
     290              :                                         &donau_urls)
     291            1 :         : GNUNET_JSON_spec_end (),
     292            1 :         GNUNET_JSON_spec_end ()
     293              :       };
     294              : 
     295            1 :       if (GNUNET_OK !=
     296            1 :           GNUNET_JSON_parse (root,
     297              :                              spec,
     298              :                              &ename,
     299              :                              &eline))
     300              :       {
     301            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     302              :                     "Failed to parse %s at %u: %s\n",
     303              :                     spec[eline].field,
     304              :                     eline,
     305              :                     ename);
     306            0 :         GNUNET_break_op (0);
     307            0 :         return GNUNET_SYSERR;
     308              :       }
     309              : 
     310            1 :       GNUNET_array_grow (output->details.donation_receipt.donau_urls,
     311              :                          output->details.donation_receipt.donau_urls_len,
     312              :                          json_array_size (donau_urls));
     313              : 
     314            1 :       for (unsigned int i = 0;
     315            4 :            i < output->details.donation_receipt.donau_urls_len;
     316            3 :            i++)
     317              :       {
     318              :         const json_t *jurl;
     319              : 
     320            3 :         jurl = json_array_get (donau_urls,
     321              :                                i);
     322            3 :         if (! json_is_string (jurl))
     323              :         {
     324            0 :           GNUNET_break_op (0);
     325            0 :           return GNUNET_SYSERR;
     326              :         }
     327            3 :         output->details.donation_receipt.donau_urls[i] =
     328            3 :           GNUNET_strdup (json_string_value (jurl));
     329              :       }
     330              : 
     331            1 :       return GNUNET_OK;
     332              :     }
     333              :   }
     334              : 
     335            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     336              :               "Field 'type' invalid in output #%u\n",
     337              :               (unsigned int) index);
     338            0 :   GNUNET_break_op (0);
     339            0 :   return GNUNET_SYSERR;
     340              : }
     341              : 
     342              : 
     343              : /**
     344              :  * Parse given JSON object to choices array.
     345              :  *
     346              :  * @param cls closure, pointer to array length
     347              :  * @param root the json array representing the choices
     348              :  * @param[out] ospec where to write the data
     349              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     350              :  */
     351              : static enum GNUNET_GenericReturnValue
     352            8 : parse_choices (
     353              :   void *cls,
     354              :   json_t *root,
     355              :   struct GNUNET_JSON_Specification *ospec)
     356              : {
     357            8 :   struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr;
     358            8 :   unsigned int *choices_len = cls;
     359              : 
     360            8 :   if (! json_is_array (root))
     361              :   {
     362            0 :     GNUNET_break_op (0);
     363            0 :     return GNUNET_SYSERR;
     364              :   }
     365              : 
     366            8 :   GNUNET_array_grow (*choices,
     367              :                      *choices_len,
     368              :                      json_array_size (root));
     369              : 
     370           17 :   for (unsigned int i = 0; i < *choices_len; i++)
     371              :   {
     372            9 :     struct TALER_MERCHANT_ContractChoice *choice = &(*choices)[i];
     373              :     const json_t *jinputs;
     374              :     const json_t *joutputs;
     375              :     struct GNUNET_JSON_Specification spec[] = {
     376            9 :       TALER_JSON_spec_amount_any ("amount",
     377              :                                   &choice->amount),
     378            9 :       GNUNET_JSON_spec_mark_optional (
     379              :         GNUNET_JSON_spec_string_copy ("description",
     380              :                                       &choice->description),
     381              :         NULL),
     382            9 :       GNUNET_JSON_spec_mark_optional (
     383              :         GNUNET_JSON_spec_object_copy ("description_i18n",
     384              :                                       &choice->description_i18n),
     385              :         NULL),
     386            9 :       TALER_JSON_spec_amount_any ("max_fee",
     387              :                                   &choice->max_fee),
     388            9 :       GNUNET_JSON_spec_array_const ("inputs",
     389              :                                     &jinputs),
     390            9 :       GNUNET_JSON_spec_array_const ("outputs",
     391              :                                     &joutputs),
     392            9 :       GNUNET_JSON_spec_end ()
     393              :     };
     394              :     const char *ename;
     395              :     unsigned int eline;
     396              : 
     397            9 :     if (GNUNET_OK !=
     398            9 :         GNUNET_JSON_parse (json_array_get (root, i),
     399              :                            spec,
     400              :                            &ename,
     401              :                            &eline))
     402              :     {
     403            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     404              :                   "Failed to parse %s at %u: %s\n",
     405              :                   spec[eline].field,
     406              :                   eline,
     407              :                   ename);
     408            0 :       GNUNET_break_op (0);
     409            0 :       return GNUNET_SYSERR;
     410              :     }
     411              : 
     412              :     {
     413              :       const json_t *jinput;
     414              :       size_t idx;
     415              : 
     416           15 :       json_array_foreach ((json_t *) jinputs, idx, jinput)
     417              :       {
     418            6 :         struct TALER_MERCHANT_ContractInput input = {
     419              :           .details.token.count = 1
     420              :         };
     421              : 
     422            6 :         if (GNUNET_OK !=
     423            6 :             TALER_MERCHANT_parse_choice_input ((json_t *) jinput,
     424              :                                                &input,
     425              :                                                idx,
     426              :                                                false))
     427              :         {
     428            0 :           GNUNET_break (0);
     429            0 :           return GNUNET_SYSERR;
     430              :         }
     431            6 :         switch (input.type)
     432              :         {
     433            0 :         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
     434            0 :           GNUNET_break_op (0);
     435            0 :           return GNUNET_SYSERR;
     436            6 :         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
     437              :           /* Ignore inputs tokens with 'count' field set to 0 */
     438            6 :           if (0 == input.details.token.count)
     439            0 :             continue;
     440            6 :           break;
     441              :         }
     442            6 :         GNUNET_array_append (choice->inputs,
     443              :                              choice->inputs_len,
     444              :                              input);
     445              :       }
     446              :     }
     447              : 
     448              :     {
     449              :       const json_t *joutput;
     450              :       size_t idx;
     451           17 :       json_array_foreach ((json_t *) joutputs, idx, joutput)
     452              :       {
     453            8 :         struct TALER_MERCHANT_ContractOutput output = {
     454              :           .details.token.count = 1
     455              :         };
     456              : 
     457            8 :         if (GNUNET_OK !=
     458            8 :             TALER_MERCHANT_parse_choice_output ((json_t *) joutput,
     459              :                                                 &output,
     460              :                                                 idx,
     461              :                                                 false))
     462              :         {
     463            0 :           GNUNET_break (0);
     464            0 :           return GNUNET_SYSERR;
     465              :         }
     466            8 :         switch (output.type)
     467              :         {
     468            0 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     469            0 :           GNUNET_break_op (0);
     470            0 :           return GNUNET_SYSERR;
     471            7 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     472              :           /* Ignore output tokens with 'count' field set to 0 */
     473            7 :           if (0 == output.details.token.count)
     474            0 :             continue;
     475            7 :           break;
     476            1 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     477            1 :           break;
     478              :         }
     479            8 :         GNUNET_array_append (choice->outputs,
     480              :                              choice->outputs_len,
     481              :                              output);
     482              :       }
     483              :     }
     484              :   }
     485              : 
     486            8 :   return GNUNET_OK;
     487              : }
     488              : 
     489              : 
     490              : /**
     491              :  * Provide specification to parse given JSON array to contract terms
     492              :  * choices. All fields from @a choices elements are copied.
     493              :  *
     494              :  * @param name name of the choices field in the JSON
     495              :  * @param[out] choices where the contract choices array has to be written
     496              :  * @param[out] choices_len length of the @a choices array
     497              :  */
     498              : static struct GNUNET_JSON_Specification
     499            8 : spec_choices (
     500              :   const char *name,
     501              :   struct TALER_MERCHANT_ContractChoice **choices,
     502              :   unsigned int *choices_len)
     503              : {
     504            8 :   struct GNUNET_JSON_Specification ret = {
     505              :     .cls = (void *) choices_len,
     506              :     .parser = &parse_choices,
     507              :     .field = name,
     508              :     .ptr = choices,
     509              :   };
     510              : 
     511            8 :   return ret;
     512              : }
     513              : 
     514              : 
     515              : void
     516           19 : TALER_MERCHANT_contract_choice_free (
     517              :   struct TALER_MERCHANT_ContractChoice *choice)
     518              : {
     519           35 :   for (unsigned int i = 0; i < choice->outputs_len; i++)
     520              :   {
     521           16 :     struct TALER_MERCHANT_ContractOutput *output = &choice->outputs[i];
     522              : 
     523           16 :     switch (output->type)
     524              :     {
     525            0 :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     526            0 :       GNUNET_break (0);
     527            0 :       break;
     528           15 :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     529           15 :       break;
     530            1 :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     531            1 :       for (unsigned int j = 0;
     532            4 :            j<output->details.donation_receipt.donau_urls_len;
     533            3 :            j++)
     534            3 :         GNUNET_free (output->details.donation_receipt.donau_urls[j]);
     535            1 :       GNUNET_array_grow (output->details.donation_receipt.donau_urls,
     536              :                          output->details.donation_receipt.donau_urls_len,
     537              :                          0);
     538            1 :       break;
     539              : #if FUTURE
     540              :     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
     541              :       GNUNET_free (output->details.coin.exchange_url);
     542              :       break;
     543              : #endif
     544              :     }
     545              :   }
     546           19 :   GNUNET_free (choice->description);
     547           19 :   if (NULL != choice->description_i18n)
     548              :   {
     549            1 :     json_decref (choice->description_i18n);
     550            1 :     choice->description_i18n = NULL;
     551              :   }
     552           19 :   GNUNET_free (choice->inputs);
     553           19 :   GNUNET_free (choice->outputs);
     554           19 : }
     555              : 
     556              : 
     557              : /**
     558              :  * Parse token details of the given JSON contract token family.
     559              :  *
     560              :  * @param cls closure, unused parameter
     561              :  * @param root the JSON object representing data
     562              :  * @param[out] ospec where to write the data
     563              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     564              :  */
     565              : static enum GNUNET_GenericReturnValue
     566            9 : parse_token_details (void *cls,
     567              :                      json_t *root,
     568              :                      struct GNUNET_JSON_Specification *ospec)
     569              : {
     570            9 :   struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
     571              :   const char *class;
     572              :   const char *ename;
     573              :   unsigned int eline;
     574              :   struct GNUNET_JSON_Specification ispec[] = {
     575            9 :     GNUNET_JSON_spec_string ("class",
     576              :                              &class),
     577            9 :     GNUNET_JSON_spec_end ()
     578              :   };
     579              : 
     580              :   (void) cls;
     581            9 :   if (GNUNET_OK !=
     582            9 :       GNUNET_JSON_parse (root,
     583              :                          ispec,
     584              :                          &ename,
     585              :                          &eline))
     586              :   {
     587            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     588              :                 "Failed to parse %s at %u: %s\n",
     589              :                 ispec[eline].field,
     590              :                 eline,
     591              :                 ename);
     592            0 :     GNUNET_break_op (0);
     593            0 :     return GNUNET_SYSERR;
     594              :   }
     595              : 
     596            9 :   family->kind = contract_token_kind_from_string (class);
     597              : 
     598            9 :   switch (family->kind)
     599              :   {
     600            0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     601            0 :     break;
     602            7 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     603              :     {
     604              :       const json_t *trusted_domains;
     605              :       struct GNUNET_JSON_Specification spec[] = {
     606            7 :         GNUNET_JSON_spec_array_const ("trusted_domains",
     607              :                                       &trusted_domains),
     608            7 :         GNUNET_JSON_spec_end ()
     609              :       };
     610              : 
     611            7 :       if (GNUNET_OK !=
     612            7 :           GNUNET_JSON_parse (root,
     613              :                              spec,
     614              :                              &ename,
     615              :                              &eline))
     616              :       {
     617            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     618              :                     "Failed to parse %s at %u: %s\n",
     619              :                     spec[eline].field,
     620              :                     eline,
     621              :                     ename);
     622            0 :         GNUNET_break_op (0);
     623            0 :         return GNUNET_SYSERR;
     624              :       }
     625              : 
     626            7 :       GNUNET_array_grow (family->details.subscription.trusted_domains,
     627              :                          family->details.subscription.trusted_domains_len,
     628              :                          json_array_size (trusted_domains));
     629              : 
     630            7 :       for (unsigned int i = 0;
     631           10 :            i < family->details.subscription.trusted_domains_len;
     632            3 :            i++)
     633              :       {
     634              :         const json_t *jdomain;
     635            3 :         jdomain = json_array_get (trusted_domains, i);
     636              : 
     637            3 :         if (! json_is_string (jdomain))
     638              :         {
     639            0 :           GNUNET_break_op (0);
     640            0 :           return GNUNET_SYSERR;
     641              :         }
     642              : 
     643            3 :         family->details.subscription.trusted_domains[i] =
     644            3 :           GNUNET_strdup (json_string_value (jdomain));
     645              :       }
     646              : 
     647            7 :       return GNUNET_OK;
     648              :     }
     649            2 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     650              :     {
     651              :       const json_t *expected_domains;
     652              :       struct GNUNET_JSON_Specification spec[] = {
     653            2 :         GNUNET_JSON_spec_array_const ("expected_domains",
     654              :                                       &expected_domains),
     655            2 :         GNUNET_JSON_spec_end ()
     656              :       };
     657              : 
     658            2 :       if (GNUNET_OK !=
     659            2 :           GNUNET_JSON_parse (root,
     660              :                              spec,
     661              :                              &ename,
     662              :                              &eline))
     663              :       {
     664            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     665              :                     "Failed to parse %s at %u: %s\n",
     666              :                     spec[eline].field,
     667              :                     eline,
     668              :                     ename);
     669            0 :         GNUNET_break_op (0);
     670            0 :         return GNUNET_SYSERR;
     671              :       }
     672              : 
     673            2 :       GNUNET_array_grow (family->details.discount.expected_domains,
     674              :                          family->details.discount.expected_domains_len,
     675              :                          json_array_size (expected_domains));
     676              : 
     677            2 :       for (unsigned int i = 0;
     678            5 :            i < family->details.discount.expected_domains_len;
     679            3 :            i++)
     680              :       {
     681              :         const json_t *jdomain;
     682              : 
     683            3 :         jdomain = json_array_get (expected_domains,
     684              :                                   i);
     685            3 :         if (! json_is_string (jdomain))
     686              :         {
     687            0 :           GNUNET_break_op (0);
     688            0 :           return GNUNET_SYSERR;
     689              :         }
     690              : 
     691            3 :         family->details.discount.expected_domains[i] =
     692            3 :           GNUNET_strdup (json_string_value (jdomain));
     693              :       }
     694              : 
     695            2 :       return GNUNET_OK;
     696              :     }
     697              :   }
     698              : 
     699            0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     700              :               "Field 'type' invalid in token family\n");
     701            0 :   GNUNET_break_op (0);
     702            0 :   return GNUNET_SYSERR;
     703              : }
     704              : 
     705              : 
     706              : /**
     707              :  * Provide specification to parse given JSON object to contract token details
     708              :  * in the contract token family. All fields from @a family are copied.
     709              :  *
     710              :  * @param name name of the token details field in the JSON
     711              :  * @param[out] family token_family where the token details have to be written
     712              :  */
     713              : static struct GNUNET_JSON_Specification
     714            9 : spec_token_details (const char *name,
     715              :                     struct TALER_MERCHANT_ContractTokenFamily *family)
     716              : {
     717            9 :   struct GNUNET_JSON_Specification ret = {
     718              :     .parser = &parse_token_details,
     719              :     .field = name,
     720              :     .ptr = family,
     721              :   };
     722              : 
     723            9 :   return ret;
     724              : }
     725              : 
     726              : 
     727              : /**
     728              :  * Parse given JSON object to token families array.
     729              :  *
     730              :  * @param cls closure, pointer to array length
     731              :  * @param root the json object representing the token families. The keys are
     732              :  *             the token family slugs.
     733              :  * @param[out] ospec where to write the data
     734              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     735              :  */
     736              : static enum GNUNET_GenericReturnValue
     737            8 : parse_token_families (void *cls,
     738              :                       json_t *root,
     739              :                       struct GNUNET_JSON_Specification *ospec)
     740              : {
     741            8 :   struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
     742            8 :   unsigned int *families_len = cls;
     743              :   json_t *jfamily;
     744              :   const char *slug;
     745              : 
     746            8 :   if (! json_is_object (root))
     747              :   {
     748            0 :     GNUNET_break_op (0);
     749            0 :     return GNUNET_SYSERR;
     750              :   }
     751              : 
     752           17 :   json_object_foreach (root, slug, jfamily)
     753              :   {
     754              :     const json_t *keys;
     755           18 :     struct TALER_MERCHANT_ContractTokenFamily family = {
     756            9 :       .slug = GNUNET_strdup (slug)
     757              :     };
     758              :     struct GNUNET_JSON_Specification spec[] = {
     759            9 :       GNUNET_JSON_spec_string_copy ("name",
     760              :                                     &family.name),
     761            9 :       GNUNET_JSON_spec_string_copy ("description",
     762              :                                     &family.description),
     763            9 :       GNUNET_JSON_spec_object_copy ("description_i18n",
     764              :                                     &family.description_i18n),
     765            9 :       GNUNET_JSON_spec_array_const ("keys",
     766              :                                     &keys),
     767            9 :       spec_token_details ("details",
     768              :                           &family),
     769            9 :       GNUNET_JSON_spec_bool ("critical",
     770              :                              &family.critical),
     771            9 :       GNUNET_JSON_spec_end ()
     772              :     };
     773              :     const char *error_name;
     774              :     unsigned int error_line;
     775              : 
     776            9 :     if (GNUNET_OK !=
     777            9 :         GNUNET_JSON_parse (jfamily,
     778              :                            spec,
     779              :                            &error_name,
     780              :                            &error_line))
     781              :     {
     782            0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     783              :                   "Failed to parse %s at %u: %s\n",
     784              :                   spec[error_line].field,
     785              :                   error_line,
     786              :                   error_name);
     787            0 :       GNUNET_break_op (0);
     788            0 :       return GNUNET_SYSERR;
     789              :     }
     790              : 
     791            9 :     GNUNET_array_grow (family.keys,
     792              :                        family.keys_len,
     793              :                        json_array_size (keys));
     794              : 
     795           17 :     for (unsigned int i = 0; i<family.keys_len; i++)
     796              :     {
     797            8 :       struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
     798              :       struct GNUNET_JSON_Specification key_spec[] = {
     799            8 :         TALER_JSON_spec_token_pub (
     800              :           NULL,
     801              :           &key->pub),
     802            8 :         GNUNET_JSON_spec_timestamp (
     803              :           "signature_validity_start",
     804              :           &key->valid_after),
     805            8 :         GNUNET_JSON_spec_timestamp (
     806              :           "signature_validity_end",
     807              :           &key->valid_before),
     808            8 :         GNUNET_JSON_spec_end ()
     809              :       };
     810              :       const char *ierror_name;
     811              :       unsigned int ierror_line;
     812              : 
     813            8 :       if (GNUNET_OK !=
     814            8 :           GNUNET_JSON_parse (json_array_get (keys,
     815              :                                              i),
     816              :                              key_spec,
     817              :                              &ierror_name,
     818              :                              &ierror_line))
     819              :       {
     820            0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     821              :                     "Failed to parse %s at %u: %s\n",
     822              :                     key_spec[ierror_line].field,
     823              :                     ierror_line,
     824              :                     ierror_name);
     825            0 :         GNUNET_break_op (0);
     826            0 :         return GNUNET_SYSERR;
     827              :       }
     828              :     }
     829              : 
     830            9 :     GNUNET_array_append (*families,
     831              :                          *families_len,
     832              :                          family);
     833              :   }
     834              : 
     835            8 :   return GNUNET_OK;
     836              : }
     837              : 
     838              : 
     839              : /**
     840              :  * Provide specification to parse given JSON array to token families in the
     841              :  * contract terms. All fields from @a families items are copied.
     842              :  *
     843              :  * @param name name of the token families field in the JSON
     844              :  * @param[out] families where the token families array has to be written
     845              :  * @param[out] families_len length of the @a families array
     846              :  */
     847              : static struct GNUNET_JSON_Specification
     848            8 : spec_token_families (
     849              :   const char *name,
     850              :   struct TALER_MERCHANT_ContractTokenFamily **families,
     851              :   unsigned int *families_len)
     852              : {
     853            8 :   struct GNUNET_JSON_Specification ret = {
     854              :     .cls = (void *) families_len,
     855              :     .parser = &parse_token_families,
     856              :     .field = name,
     857              :     .ptr = families,
     858              :   };
     859              : 
     860            8 :   return ret;
     861              : }
     862              : 
     863              : 
     864              : enum GNUNET_GenericReturnValue
     865            2 : TALER_MERCHANT_find_token_family_key (
     866              :   const char *slug,
     867              :   struct GNUNET_TIME_Timestamp valid_after,
     868              :   const struct TALER_MERCHANT_ContractTokenFamily *families,
     869              :   unsigned int families_len,
     870              :   struct TALER_MERCHANT_ContractTokenFamily *family,
     871              :   struct TALER_MERCHANT_ContractTokenFamilyKey *key)
     872              : {
     873            3 :   for (unsigned int i = 0; i < families_len; i++)
     874              :   {
     875            3 :     const struct TALER_MERCHANT_ContractTokenFamily *fami
     876            3 :       = &families[i];
     877              : 
     878            3 :     if (0 != strcmp (fami->slug,
     879              :                      slug))
     880            1 :       continue;
     881            2 :     if (NULL != family)
     882            2 :       *family = *fami;
     883            2 :     for (unsigned int k = 0; k < fami->keys_len; k++)
     884              :     {
     885            2 :       struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
     886              : 
     887            2 :       if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
     888              :                                      ==,
     889              :                                      valid_after))
     890              :       {
     891            2 :         if (NULL != key)
     892            2 :           *key = *ki;
     893            2 :         return GNUNET_OK;
     894              :       }
     895              :     }
     896              :     /* matching family found, but no key. */
     897            0 :     return GNUNET_NO;
     898              :   }
     899              : 
     900              :   /* no matching family found */
     901            0 :   return GNUNET_SYSERR;
     902              : }
     903              : 
     904              : 
     905              : /**
     906              :  * Free all the fields in the given @a family, but not @a family itself, since
     907              :  * it is normally part of an array.
     908              :  *
     909              :  * @param[in] family contract token family to free
     910              :  */
     911              : static void
     912            9 : contract_token_family_free (
     913              :   struct TALER_MERCHANT_ContractTokenFamily *family)
     914              : {
     915            9 :   GNUNET_free (family->slug);
     916            9 :   GNUNET_free (family->name);
     917            9 :   GNUNET_free (family->description);
     918            9 :   if (NULL != family->description_i18n)
     919              :   {
     920            9 :     json_decref (family->description_i18n);
     921            9 :     family->description_i18n = NULL;
     922              :   }
     923           17 :   for (unsigned int i = 0; i < family->keys_len; i++)
     924            8 :     TALER_token_issue_pub_free (&family->keys[i].pub);
     925            9 :   GNUNET_free (family->keys);
     926              : 
     927            9 :   switch (family->kind)
     928              :   {
     929            0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     930            0 :     break;
     931            2 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     932            5 :     for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
     933            3 :          i++)
     934            3 :       GNUNET_free (family->details.discount.expected_domains[i]);
     935            2 :     break;
     936            7 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     937           10 :     for (unsigned int i = 0; i < family->details.subscription.
     938            3 :          trusted_domains_len; i++)
     939            3 :       GNUNET_free (family->details.subscription.trusted_domains[i]);
     940            7 :     break;
     941              :   }
     942            9 : }
     943              : 
     944              : 
     945              : /**
     946              :  * Parse contract version of given JSON contract terms.
     947              :  *
     948              :  * @param cls closure, unused parameter
     949              :  * @param root the JSON object representing data
     950              :  * @param[out] spec where to write the data
     951              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     952              :  */
     953              : static enum GNUNET_GenericReturnValue
     954          165 : parse_contract_version (void *cls,
     955              :                         json_t *root,
     956              :                         struct GNUNET_JSON_Specification *spec)
     957              : {
     958          165 :   enum TALER_MERCHANT_ContractVersion *res
     959              :     = (enum TALER_MERCHANT_ContractVersion *) spec->ptr;
     960              : 
     961              :   (void) cls;
     962          165 :   if (json_is_integer (root))
     963              :   {
     964          165 :     json_int_t version = json_integer_value (root);
     965              : 
     966          165 :     switch (version)
     967              :     {
     968          157 :     case 0:
     969          157 :       *res = TALER_MERCHANT_CONTRACT_VERSION_0;
     970          157 :       return GNUNET_OK;
     971            8 :     case 1:
     972            8 :       *res = TALER_MERCHANT_CONTRACT_VERSION_1;
     973            8 :       return GNUNET_OK;
     974              :     }
     975              : 
     976            0 :     GNUNET_break_op (0);
     977            0 :     return GNUNET_SYSERR;
     978              :   }
     979              : 
     980            0 :   if (json_is_null (root))
     981              :   {
     982            0 :     *res = TALER_MERCHANT_CONTRACT_VERSION_0;
     983            0 :     return GNUNET_OK;
     984              :   }
     985              : 
     986            0 :   GNUNET_break_op (0);
     987            0 :   return GNUNET_SYSERR;
     988              : }
     989              : 
     990              : 
     991              : /**
     992              :  * Create JSON specification to parse a merchant contract
     993              :  * version.
     994              :  *
     995              :  * @param name name of the field
     996              :  * @param[out] version where to write the contract version
     997              :  * @return JSON specification object
     998              :  */
     999              : static struct GNUNET_JSON_Specification
    1000          165 : spec_contract_version (
    1001              :   const char *name,
    1002              :   enum TALER_MERCHANT_ContractVersion *version)
    1003              : {
    1004          165 :   struct GNUNET_JSON_Specification ret = {
    1005              :     .parser = &parse_contract_version,
    1006              :     .field = name,
    1007              :     .ptr = version
    1008              :   };
    1009              : 
    1010          165 :   *version = TALER_MERCHANT_CONTRACT_VERSION_0;
    1011          165 :   return ret;
    1012              : }
    1013              : 
    1014              : 
    1015              : /**
    1016              :  * Parse v0-specific fields of @a input JSON into @a contract.
    1017              :  *
    1018              :  * @param[in] input the JSON contract terms
    1019              :  * @param[out] contract where to write the data
    1020              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    1021              :  */
    1022              : static enum GNUNET_GenericReturnValue
    1023          157 : parse_contract_v0 (
    1024              :   json_t *input,
    1025              :   struct TALER_MERCHANT_Contract *contract)
    1026              : {
    1027              :   struct GNUNET_JSON_Specification espec[] = {
    1028          157 :     TALER_JSON_spec_amount_any ("amount",
    1029              :                                 &contract->details.v0.brutto),
    1030          157 :     TALER_JSON_spec_amount_any ("max_fee",
    1031              :                                 &contract->details.v0.max_fee),
    1032          157 :     GNUNET_JSON_spec_end ()
    1033              :   };
    1034              :   enum GNUNET_GenericReturnValue res;
    1035              :   const char *ename;
    1036              :   unsigned int eline;
    1037              : 
    1038          157 :   res = GNUNET_JSON_parse (input,
    1039              :                            espec,
    1040              :                            &ename,
    1041              :                            &eline);
    1042          157 :   if (GNUNET_OK != res)
    1043              :   {
    1044            0 :     GNUNET_break (0);
    1045            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1046              :                 "Failed to parse contract v0 at field %s\n",
    1047              :                 ename);
    1048            0 :     return GNUNET_SYSERR;
    1049              :   }
    1050              : 
    1051          157 :   if (GNUNET_OK !=
    1052          157 :       TALER_amount_cmp_currency (&contract->details.v0.max_fee,
    1053          157 :                                  &contract->details.v0.brutto))
    1054              :   {
    1055            0 :     GNUNET_break (0);
    1056            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1057              :                 "'max_fee' in database does not match currency of contract price");
    1058            0 :     return GNUNET_SYSERR;
    1059              :   }
    1060              : 
    1061          157 :   return res;
    1062              : }
    1063              : 
    1064              : 
    1065              : /**
    1066              :  * Parse v1-specific fields of @a input JSON into @a contract.
    1067              :  *
    1068              :  * @param[in] input the JSON contract terms
    1069              :  * @param[out] contract where to write the data
    1070              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    1071              :  */
    1072              : static enum GNUNET_GenericReturnValue
    1073            8 : parse_contract_v1 (
    1074              :   json_t *input,
    1075              :   struct TALER_MERCHANT_Contract *contract)
    1076              : {
    1077              :   struct GNUNET_JSON_Specification espec[] = {
    1078            8 :     spec_choices (
    1079              :       "choices",
    1080              :       &contract->details.v1.choices,
    1081              :       &contract->details.v1.choices_len),
    1082            8 :     spec_token_families (
    1083              :       "token_families",
    1084              :       &contract->details.v1.token_authorities,
    1085              :       &contract->details.v1.token_authorities_len),
    1086            8 :     GNUNET_JSON_spec_end ()
    1087              :   };
    1088              : 
    1089              :   enum GNUNET_GenericReturnValue res;
    1090              :   const char *ename;
    1091              :   unsigned int eline;
    1092              : 
    1093            8 :   res = GNUNET_JSON_parse (input,
    1094              :                            espec,
    1095              :                            &ename,
    1096              :                            &eline);
    1097            8 :   if (GNUNET_OK != res)
    1098              :   {
    1099            0 :     GNUNET_break (0);
    1100            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1101              :                 "Failed to parse contract v1 at field %s\n",
    1102              :                 ename);
    1103            0 :     return GNUNET_SYSERR;
    1104              :   }
    1105              : 
    1106            8 :   return res;
    1107              : }
    1108              : 
    1109              : 
    1110              : /**
    1111              :  * Parse the given unit quantity string @a s and store the result in @a q.
    1112              :  *
    1113              :  * @param s quantity to parse
    1114              :  * @param[out] q where to store the result
    1115              :  * @return #GNUNET_OK on success
    1116              :  */
    1117              : static enum GNUNET_GenericReturnValue
    1118           17 : parse_unit_quantity (const char *s,
    1119              :                      struct TALER_MERCHANT_ProductQuantity *q)
    1120              : {
    1121              :   const char *ptr;
    1122           17 :   uint64_t integer = 0;
    1123           17 :   uint32_t frac = 0;
    1124           17 :   unsigned int digits = 0;
    1125              : 
    1126           17 :   if (NULL == s)
    1127              :   {
    1128            0 :     GNUNET_break_op (0);
    1129            0 :     return GNUNET_SYSERR;
    1130              :   }
    1131           17 :   ptr = s;
    1132           17 :   if ('\0' == *ptr)
    1133              :   {
    1134            0 :     GNUNET_break_op (0);
    1135            0 :     return GNUNET_SYSERR;
    1136              :   }
    1137           17 :   if ('-' == *ptr)
    1138              :   {
    1139            0 :     GNUNET_break_op (0);
    1140            0 :     return GNUNET_SYSERR;
    1141              :   }
    1142           17 :   if (! isdigit ((unsigned char) *ptr))
    1143              :   {
    1144            0 :     GNUNET_break_op (0);
    1145            0 :     return GNUNET_SYSERR;
    1146              :   }
    1147           34 :   while (isdigit ((unsigned char) *ptr))
    1148              :   {
    1149           17 :     unsigned int digit = (unsigned int) (*ptr - '0');
    1150              : 
    1151              :     /* We intentionally allow at most INT64_MAX (as -1 has special meanings),
    1152              :        even though the data type would support UINT64_MAX */
    1153           17 :     if (integer > (INT64_MAX - digit) / 10)
    1154              :     {
    1155            0 :       GNUNET_break_op (0);
    1156            0 :       return GNUNET_SYSERR;
    1157              :     }
    1158           17 :     integer = integer * 10 + digit;
    1159           17 :     ptr++;
    1160              :   }
    1161           17 :   if ('.' == *ptr)
    1162              :   {
    1163            4 :     ptr++;
    1164            4 :     if ('\0' == *ptr)
    1165              :     {
    1166            0 :       GNUNET_break_op (0);
    1167            0 :       return GNUNET_SYSERR;
    1168              :     }
    1169            8 :     while (isdigit ((unsigned char) *ptr))
    1170              :     {
    1171            4 :       unsigned int digit = (unsigned int) (*ptr - '0');
    1172              : 
    1173            4 :       if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
    1174              :       {
    1175            0 :         GNUNET_break_op (0);
    1176            0 :         return GNUNET_SYSERR;
    1177              :       }
    1178            4 :       frac = (uint32_t) (frac * 10 + digit);
    1179            4 :       digits++;
    1180            4 :       ptr++;
    1181              :     }
    1182           24 :     while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
    1183              :     {
    1184           20 :       frac *= 10;
    1185           20 :       digits++;
    1186              :     }
    1187              :   }
    1188           17 :   if ('\0' != *ptr)
    1189              :   {
    1190            0 :     GNUNET_break_op (0);
    1191            0 :     return GNUNET_SYSERR;
    1192              :   }
    1193           17 :   q->integer = integer;
    1194           17 :   q->fractional = frac;
    1195           17 :   return GNUNET_OK;
    1196              : }
    1197              : 
    1198              : 
    1199              : bool
    1200           75 : TALER_MERCHANT_taxes_array_valid (const json_t *taxes)
    1201              : {
    1202              :   json_t *tax;
    1203              :   size_t idx;
    1204              : 
    1205           75 :   if (! json_is_array (taxes))
    1206            0 :     return false;
    1207           75 :   json_array_foreach (taxes, idx, tax)
    1208              :   {
    1209              :     struct TALER_Amount amount;
    1210              :     const char *name;
    1211              :     struct GNUNET_JSON_Specification spec[] = {
    1212            0 :       GNUNET_JSON_spec_string ("name",
    1213              :                                &name),
    1214            0 :       TALER_JSON_spec_amount_any ("tax",
    1215              :                                   &amount),
    1216            0 :       GNUNET_JSON_spec_end ()
    1217              :     };
    1218              :     enum GNUNET_GenericReturnValue res;
    1219              :     const char *ename;
    1220              :     unsigned int eline;
    1221              : 
    1222            0 :     res = GNUNET_JSON_parse (tax,
    1223              :                              spec,
    1224              :                              &ename,
    1225              :                              &eline);
    1226            0 :     if (GNUNET_OK != res)
    1227              :     {
    1228            0 :       GNUNET_break_op (0);
    1229            0 :       return false;
    1230              :     }
    1231              :   }
    1232           75 :   return true;
    1233              : }
    1234              : 
    1235              : 
    1236              : enum GNUNET_GenericReturnValue
    1237           22 : TALER_MERCHANT_parse_product_sold (const json_t *pj,
    1238              :                                    struct TALER_MERCHANT_ProductSold *r)
    1239              : {
    1240              :   bool no_quantity;
    1241              :   bool no_unit_quantity;
    1242              :   bool no_price;
    1243              :   uint64_t legacy_quantity;
    1244              :   const char *unit_quantity_s;
    1245              :   struct TALER_Amount price;
    1246           22 :   const json_t *prices = NULL;
    1247              :   struct GNUNET_JSON_Specification spec[] = {
    1248           22 :     GNUNET_JSON_spec_mark_optional (
    1249              :       GNUNET_JSON_spec_string_copy ("product_id",
    1250              :                                     &r->product_id),
    1251              :       NULL),
    1252           22 :     GNUNET_JSON_spec_mark_optional (
    1253              :       GNUNET_JSON_spec_string_copy ("product_name",
    1254              :                                     &r->product_name),
    1255              :       NULL),
    1256           22 :     GNUNET_JSON_spec_mark_optional (
    1257              :       GNUNET_JSON_spec_string_copy ("description",
    1258              :                                     &r->description),
    1259              :       NULL),
    1260           22 :     GNUNET_JSON_spec_mark_optional (
    1261              :       GNUNET_JSON_spec_object_copy ("description_i18n",
    1262              :                                     &r->description_i18n),
    1263              :       NULL),
    1264           22 :     GNUNET_JSON_spec_mark_optional (
    1265              :       GNUNET_JSON_spec_uint64 ("quantity",
    1266              :                                &legacy_quantity),
    1267              :       &no_quantity),
    1268           22 :     GNUNET_JSON_spec_mark_optional (
    1269              :       GNUNET_JSON_spec_string ("unit_quantity",
    1270              :                                &unit_quantity_s),
    1271              :       &no_unit_quantity),
    1272           22 :     GNUNET_JSON_spec_mark_optional (
    1273              :       GNUNET_JSON_spec_string_copy ("unit",
    1274              :                                     &r->unit),
    1275              :       NULL),
    1276           22 :     GNUNET_JSON_spec_mark_optional (
    1277              :       TALER_JSON_spec_amount_any ("price",
    1278              :                                   &price),
    1279              :       &no_price),
    1280           22 :     GNUNET_JSON_spec_mark_optional (
    1281              :       GNUNET_JSON_spec_array_const ("prices",
    1282              :                                     &prices),
    1283              :       NULL),
    1284           22 :     GNUNET_JSON_spec_mark_optional (
    1285              :       GNUNET_JSON_spec_string_copy ("image",
    1286              :                                     &r->image),
    1287              :       NULL),
    1288           22 :     GNUNET_JSON_spec_mark_optional (
    1289              :       GNUNET_JSON_spec_array_copy ("taxes",
    1290              :                                    &r->taxes),
    1291              :       NULL),
    1292           22 :     GNUNET_JSON_spec_mark_optional (
    1293              :       GNUNET_JSON_spec_timestamp ("delivery_date",
    1294              :                                   &r->delivery_date),
    1295              :       NULL),
    1296           22 :     GNUNET_JSON_spec_mark_optional (
    1297              :       GNUNET_JSON_spec_uint64 ("product_money_pot",
    1298              :                                &r->product_money_pot),
    1299              :       NULL),
    1300           22 :     GNUNET_JSON_spec_end ()
    1301              :   };
    1302              :   enum GNUNET_GenericReturnValue res;
    1303              :   const char *ename;
    1304              :   unsigned int eline;
    1305              : 
    1306           22 :   r->delivery_date = GNUNET_TIME_UNIT_FOREVER_TS;
    1307           22 :   res = GNUNET_JSON_parse (pj,
    1308              :                            spec,
    1309              :                            &ename,
    1310              :                            &eline);
    1311           22 :   if (GNUNET_OK != res)
    1312              :   {
    1313            0 :     GNUNET_break (0);
    1314            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1315              :                 "Failed to parse product at field %s\n",
    1316              :                 ename);
    1317            0 :     return GNUNET_SYSERR;
    1318              :   }
    1319           22 :   if (! no_quantity)
    1320              :   {
    1321           18 :     r->unit_quantity.integer = legacy_quantity;
    1322           18 :     r->unit_quantity.fractional = 0;
    1323              :   }
    1324           22 :   if (! no_unit_quantity)
    1325              :   {
    1326           17 :     if (GNUNET_OK !=
    1327           17 :         parse_unit_quantity (unit_quantity_s,
    1328              :                              &r->unit_quantity))
    1329              :     {
    1330            0 :       GNUNET_break (0);
    1331            0 :       return GNUNET_SYSERR;
    1332              :     }
    1333              :   }
    1334           22 :   if ( (! no_quantity) && (! no_unit_quantity) )
    1335              :   {
    1336           13 :     GNUNET_break ( (0 == r->unit_quantity.fractional) &&
    1337              :                    (legacy_quantity == r->unit_quantity.integer) );
    1338              :   }
    1339           22 :   if ( (NULL != r->image) &&
    1340           20 :        (! TALER_MERCHANT_image_data_url_valid (r->image)) )
    1341              :   {
    1342            0 :     GNUNET_break_op (0);
    1343            0 :     return GNUNET_SYSERR;
    1344              :   }
    1345           22 :   if ( (NULL != r->taxes) &&
    1346           20 :        (! TALER_MERCHANT_taxes_array_valid (r->taxes)) )
    1347              :   {
    1348            0 :     GNUNET_break_op (0);
    1349            0 :     return GNUNET_SYSERR;
    1350              :   }
    1351           22 :   if (NULL != prices)
    1352              :   {
    1353           17 :     size_t len = json_array_size (prices);
    1354              :     size_t i;
    1355              :     json_t *price_i;
    1356              : 
    1357           17 :     GNUNET_assert (len < UINT_MAX);
    1358           17 :     r->prices = GNUNET_new_array ((unsigned int) len,
    1359              :                                   struct TALER_Amount);
    1360           30 :     json_array_foreach (prices, i, price_i)
    1361              :     {
    1362              :       struct GNUNET_JSON_Specification pspec[] = {
    1363           13 :         TALER_JSON_spec_amount_any (NULL,
    1364           13 :                                     &r->prices[i]),
    1365           13 :         GNUNET_JSON_spec_end ()
    1366              :       };
    1367              : 
    1368           13 :       res = GNUNET_JSON_parse (price_i,
    1369              :                                pspec,
    1370              :                                &ename,
    1371              :                                &eline);
    1372           13 :       if (GNUNET_OK != res)
    1373              :       {
    1374            0 :         GNUNET_break (0);
    1375            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1376              :                     "Failed to parse price at index %u\n",
    1377              :                     (unsigned int) i);
    1378            0 :         return GNUNET_SYSERR;
    1379              :       }
    1380              :     }
    1381              :   }
    1382            5 :   else if (! no_price)
    1383              :   {
    1384            5 :     r->prices = GNUNET_new_array (1,
    1385              :                                   struct TALER_Amount);
    1386            5 :     r->prices[0] = price;
    1387              :   }
    1388           22 :   return GNUNET_OK;
    1389              : }
    1390              : 
    1391              : 
    1392              : struct TALER_MERCHANT_Contract *
    1393          165 : TALER_MERCHANT_contract_parse (json_t *input,
    1394              :                                bool nonce_optional)
    1395              : {
    1396              :   struct TALER_MERCHANT_Contract *contract
    1397          165 :     = GNUNET_new (struct TALER_MERCHANT_Contract);
    1398          165 :   const json_t *products = NULL;
    1399              :   struct GNUNET_JSON_Specification espec[] = {
    1400          165 :     spec_contract_version ("version",
    1401              :                            &contract->version),
    1402          165 :     GNUNET_JSON_spec_string_copy ("summary",
    1403              :                                   &contract->summary),
    1404              :     /* FIXME: do i18n_str validation in the future */
    1405          165 :     GNUNET_JSON_spec_mark_optional (
    1406              :       GNUNET_JSON_spec_object_copy ("summary_i18n",
    1407              :                                     &contract->summary_i18n),
    1408              :       NULL),
    1409          165 :     GNUNET_JSON_spec_string_copy ("order_id",
    1410              :                                   &contract->order_id),
    1411          165 :     GNUNET_JSON_spec_mark_optional (
    1412              :       GNUNET_JSON_spec_string_copy ("public_reorder_url",
    1413              :                                     &contract->public_reorder_url),
    1414              :       NULL),
    1415          165 :     GNUNET_JSON_spec_mark_optional (
    1416              :       GNUNET_JSON_spec_string_copy ("fulfillment_url",
    1417              :                                     &contract->fulfillment_url),
    1418              :       NULL),
    1419          165 :     GNUNET_JSON_spec_mark_optional (
    1420              :       GNUNET_JSON_spec_string_copy ("fulfillment_message",
    1421              :                                     &contract->fulfillment_message),
    1422              :       NULL),
    1423          165 :     GNUNET_JSON_spec_mark_optional (
    1424              :       GNUNET_JSON_spec_object_copy ("fulfillment_message_i18n",
    1425              :                                     &contract->fulfillment_message_i18n),
    1426              :       NULL),
    1427          165 :     GNUNET_JSON_spec_mark_optional (
    1428              :       GNUNET_JSON_spec_array_const ("products",
    1429              :                                     &products),
    1430              :       NULL),
    1431          165 :     GNUNET_JSON_spec_timestamp ("timestamp",
    1432              :                                 &contract->timestamp),
    1433          165 :     GNUNET_JSON_spec_timestamp ("refund_deadline",
    1434              :                                 &contract->refund_deadline),
    1435          165 :     GNUNET_JSON_spec_timestamp ("pay_deadline",
    1436              :                                 &contract->pay_deadline),
    1437          165 :     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    1438              :                                 &contract->wire_deadline),
    1439          165 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
    1440              :                                  &contract->merchant_pub),
    1441          165 :     GNUNET_JSON_spec_string_copy ("merchant_base_url",
    1442              :                                   &contract->merchant_base_url),
    1443          165 :     spec_merchant_details ("merchant",
    1444              :                            contract),
    1445          165 :     GNUNET_JSON_spec_fixed_auto ("h_wire",
    1446              :                                  &contract->h_wire),
    1447          165 :     GNUNET_JSON_spec_string_copy ("wire_method",
    1448              :                                   &contract->wire_method),
    1449          165 :     GNUNET_JSON_spec_array_copy ("exchanges",
    1450              :                                  &contract->exchanges),
    1451          165 :     GNUNET_JSON_spec_mark_optional (
    1452              :       GNUNET_JSON_spec_object_copy ("delivery_location",
    1453              :                                     &contract->delivery_location),
    1454              :       NULL),
    1455          165 :     GNUNET_JSON_spec_mark_optional (
    1456              :       GNUNET_JSON_spec_timestamp ("delivery_date",
    1457              :                                   &contract->delivery_date),
    1458              :       NULL),
    1459              :     (nonce_optional)
    1460          163 :     ? GNUNET_JSON_spec_mark_optional (
    1461              :       GNUNET_JSON_spec_string_copy ("nonce",
    1462              :                                     &contract->nonce),
    1463              :       NULL)
    1464          165 :     : GNUNET_JSON_spec_string_copy ("nonce",
    1465              :                                     &contract->nonce),
    1466          165 :     GNUNET_JSON_spec_mark_optional (
    1467              :       GNUNET_JSON_spec_relative_time ("auto_refund",
    1468              :                                       &contract->auto_refund),
    1469              :       NULL),
    1470          165 :     GNUNET_JSON_spec_mark_optional (
    1471              :       GNUNET_JSON_spec_object_copy ("extra",
    1472              :                                     &contract->extra),
    1473              :       NULL),
    1474          165 :     GNUNET_JSON_spec_mark_optional (
    1475              :       GNUNET_JSON_spec_uint8 ("minimum_age",
    1476              :                               &contract->minimum_age),
    1477              :       NULL),
    1478          165 :     GNUNET_JSON_spec_mark_optional (
    1479              :       GNUNET_JSON_spec_uint64 ("default_money_pot",
    1480              :                                &contract->default_money_pot),
    1481              :       NULL),
    1482          165 :     GNUNET_JSON_spec_end ()
    1483              :   };
    1484              : 
    1485              :   enum GNUNET_GenericReturnValue res;
    1486              :   const char *ename;
    1487              :   unsigned int eline;
    1488              : 
    1489          165 :   GNUNET_assert (NULL != input);
    1490          165 :   res = GNUNET_JSON_parse (input,
    1491              :                            espec,
    1492              :                            &ename,
    1493              :                            &eline);
    1494          165 :   if (GNUNET_OK != res)
    1495              :   {
    1496            0 :     GNUNET_break (0);
    1497            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1498              :                 "Failed to parse contract at field %s\n",
    1499              :                 ename);
    1500            0 :     goto cleanup;
    1501              :   }
    1502          165 :   if (NULL != products)
    1503              :   {
    1504          165 :     contract->products_len = json_array_size (products);
    1505          165 :     if (0 != contract->products_len)
    1506              :     {
    1507              :       size_t i;
    1508              :       json_t *p;
    1509              : 
    1510           17 :       contract->products = GNUNET_new_array (contract->products_len,
    1511              :                                              struct TALER_MERCHANT_ProductSold);
    1512           34 :       json_array_foreach (products, i, p)
    1513              :       {
    1514           17 :         if (GNUNET_OK !=
    1515           17 :             TALER_MERCHANT_parse_product_sold (p,
    1516           17 :                                                &contract->products[i]))
    1517              :         {
    1518            0 :           GNUNET_break (0);
    1519            0 :           GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1520              :                       "Failed to parse product at offset %u\n",
    1521              :                       (unsigned int) i);
    1522            0 :           goto cleanup;
    1523              :         }
    1524              :       }
    1525              :     }
    1526              :   }
    1527          165 :   switch (contract->version)
    1528              :   {
    1529          157 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1530          157 :     res = parse_contract_v0 (input,
    1531              :                              contract);
    1532          157 :     break;
    1533            8 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1534            8 :     res = parse_contract_v1 (input,
    1535              :                              contract);
    1536            8 :     break;
    1537              :   }
    1538              : 
    1539          165 :   if (GNUNET_OK != res)
    1540              :   {
    1541            0 :     GNUNET_break (0);
    1542            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1543              :                 "Failed to parse contract\n");
    1544            0 :     goto cleanup;
    1545              :   }
    1546          165 :   return contract;
    1547              : 
    1548            0 : cleanup:
    1549            0 :   TALER_MERCHANT_contract_free (contract);
    1550            0 :   return NULL;
    1551              : }
    1552              : 
    1553              : 
    1554              : void
    1555           22 : TALER_MERCHANT_product_sold_free (struct TALER_MERCHANT_ProductSold *product)
    1556              : {
    1557           22 :   GNUNET_free (product->product_id);
    1558           22 :   GNUNET_free (product->product_name);
    1559           22 :   GNUNET_free (product->description);
    1560           22 :   json_decref (product->description_i18n);
    1561           22 :   GNUNET_free (product->prices);
    1562           22 :   GNUNET_free (product->unit);
    1563           22 :   GNUNET_free (product->image);
    1564           22 :   json_decref (product->taxes);
    1565           22 : }
    1566              : 
    1567              : 
    1568              : void
    1569          165 : TALER_MERCHANT_contract_free (
    1570              :   struct TALER_MERCHANT_Contract *contract)
    1571              : {
    1572          165 :   if (NULL == contract)
    1573            0 :     return;
    1574          165 :   GNUNET_free (contract->public_reorder_url);
    1575          165 :   GNUNET_free (contract->order_id);
    1576          165 :   GNUNET_free (contract->merchant_base_url);
    1577          165 :   GNUNET_free (contract->merchant.name);
    1578          165 :   GNUNET_free (contract->merchant.website);
    1579          165 :   GNUNET_free (contract->merchant.email);
    1580          165 :   GNUNET_free (contract->merchant.logo);
    1581          165 :   if (NULL != contract->merchant.address)
    1582              :   {
    1583          165 :     json_decref (contract->merchant.address);
    1584          165 :     contract->merchant.address = NULL;
    1585              :   }
    1586          165 :   if (NULL != contract->merchant.jurisdiction)
    1587              :   {
    1588          165 :     json_decref (contract->merchant.jurisdiction);
    1589          165 :     contract->merchant.jurisdiction = NULL;
    1590              :   }
    1591          165 :   GNUNET_free (contract->summary);
    1592          165 :   GNUNET_free (contract->fulfillment_url);
    1593          165 :   GNUNET_free (contract->fulfillment_message);
    1594          165 :   if (NULL != contract->fulfillment_message_i18n)
    1595              :   {
    1596            2 :     json_decref (contract->fulfillment_message_i18n);
    1597            2 :     contract->fulfillment_message_i18n = NULL;
    1598              :   }
    1599          165 :   if (NULL != contract->products)
    1600              :   {
    1601           34 :     for (size_t i = 0; i<contract->products_len; i++)
    1602           17 :       TALER_MERCHANT_product_sold_free (&contract->products[i]);
    1603           17 :     GNUNET_free (contract->products);
    1604              :   }
    1605          165 :   GNUNET_free (contract->wire_method);
    1606          165 :   if (NULL != contract->exchanges)
    1607              :   {
    1608          165 :     json_decref (contract->exchanges);
    1609          165 :     contract->exchanges = NULL;
    1610              :   }
    1611          165 :   if (NULL != contract->delivery_location)
    1612              :   {
    1613            2 :     json_decref (contract->delivery_location);
    1614            2 :     contract->delivery_location = NULL;
    1615              :   }
    1616          165 :   GNUNET_free (contract->nonce);
    1617          165 :   if (NULL != contract->extra)
    1618              :   {
    1619            2 :     json_decref (contract->extra);
    1620            2 :     contract->extra = NULL;
    1621              :   }
    1622              : 
    1623          165 :   switch (contract->version)
    1624              :   {
    1625          157 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1626          157 :     break;
    1627            8 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1628            8 :     for (unsigned int i = 0;
    1629           17 :          i < contract->details.v1.choices_len;
    1630            9 :          i++)
    1631            9 :       TALER_MERCHANT_contract_choice_free (&contract->details.v1.choices[i]);
    1632            8 :     GNUNET_free (contract->details.v1.choices);
    1633            8 :     for (unsigned int i = 0;
    1634           17 :          i < contract->details.v1.token_authorities_len;
    1635            9 :          i++)
    1636            9 :       contract_token_family_free (&contract->details.v1.token_authorities[i]);
    1637            8 :     GNUNET_free (contract->details.v1.token_authorities);
    1638            8 :     break;
    1639              :   }
    1640          165 :   GNUNET_free (contract);
    1641              : }
        

Generated by: LCOV version 2.0-1