LCOV - code coverage report
Current view: top level - util - contract_parse.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 58.9 % 627 369
Test Date: 2026-04-12 12:58:13 Functions: 83.3 % 24 20

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

Generated by: LCOV version 2.0-1