LCOV - code coverage report
Current view: top level - util - contract_parse.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 349 453 77.0 %
Date: 2025-07-31 16:16:42 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   (C) 2024 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             :  */
      21             : #include "platform.h"
      22             : #include <gnunet/gnunet_common.h>
      23             : #include <gnunet/gnunet_json_lib.h>
      24             : #include <jansson.h>
      25             : #include <stdbool.h>
      26             : #include <stdint.h>
      27             : #include <taler/taler_json_lib.h>
      28             : #include <taler/taler_util.h>
      29             : #include "taler_merchant_util.h"
      30             : 
      31             : 
      32             : /**
      33             :  * Parse merchant details of given JSON contract terms.
      34             :  *
      35             :  * @param cls closure, unused parameter
      36             :  * @param root the JSON object representing data
      37             :  * @param[out] spec where to write the data
      38             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
      39             :  */
      40             : static enum GNUNET_GenericReturnValue
      41         152 : parse_merchant_details (void *cls,
      42             :                         json_t *root,
      43             :                         struct GNUNET_JSON_Specification *ospec)
      44             : {
      45         152 :   struct TALER_MERCHANT_Contract *contract = ospec->ptr;
      46             :   struct GNUNET_JSON_Specification spec[] = {
      47         152 :     GNUNET_JSON_spec_string_copy ("name",
      48             :                                   &contract->merchant.name),
      49         152 :     GNUNET_JSON_spec_mark_optional (
      50             :       GNUNET_JSON_spec_string_copy ("email",
      51             :                                     &contract->merchant.email),
      52             :       NULL),
      53         152 :     GNUNET_JSON_spec_mark_optional (
      54             :       GNUNET_JSON_spec_string_copy ("website",
      55             :                                     &contract->merchant.website),
      56             :       NULL),
      57         152 :     GNUNET_JSON_spec_mark_optional (
      58             :       GNUNET_JSON_spec_string_copy ("logo",
      59             :                                     &contract->merchant.logo),
      60             :       NULL),
      61         152 :     GNUNET_JSON_spec_mark_optional (
      62             :       GNUNET_JSON_spec_object_copy ("address",
      63             :                                     &contract->merchant.address),
      64             :       NULL),
      65         152 :     GNUNET_JSON_spec_mark_optional (
      66             :       GNUNET_JSON_spec_object_copy ("jurisdiction",
      67             :                                     &contract->merchant.jurisdiction),
      68             :       NULL),
      69         152 :     GNUNET_JSON_spec_end ()
      70             :   };
      71             :   const char *error_name;
      72             :   unsigned int error_line;
      73             : 
      74             :   (void) cls;
      75         152 :   if (GNUNET_OK !=
      76         152 :       GNUNET_JSON_parse (root,
      77             :                          spec,
      78             :                          &error_name,
      79             :                          &error_line))
      80             :   {
      81           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
      82             :                 "Failed to parse %s at %u: %s\n",
      83             :                 spec[error_line].field,
      84             :                 error_line,
      85             :                 error_name);
      86           0 :     GNUNET_break_op (0);
      87           0 :     return GNUNET_SYSERR;
      88             :   }
      89         152 :   return GNUNET_OK;
      90             : }
      91             : 
      92             : 
      93             : /**
      94             :  * Provide specification to parse given JSON object to merchant details in the
      95             :  * contract terms. All fields from @a contract are copied.
      96             :  *
      97             :  * @param name name of the merchant details field in the JSON
      98             :  * @param[out] contract where the merchant details have to be written
      99             :  */
     100             : static struct GNUNET_JSON_Specification
     101         152 : spec_merchant_details (const char *name,
     102             :                        struct TALER_MERCHANT_Contract *contract)
     103             : {
     104         152 :   struct GNUNET_JSON_Specification ret = {
     105             :     .parser = &parse_merchant_details,
     106             :     .field = name,
     107             :     .ptr = contract,
     108             :   };
     109             : 
     110         152 :   return ret;
     111             : }
     112             : 
     113             : 
     114             : /**
     115             :  * Get enum value from contract token type string.
     116             :  *
     117             :  * @param str contract token type string
     118             :  * @return enum value of token type
     119             :  */
     120             : static enum TALER_MERCHANT_ContractTokenKind
     121           9 : contract_token_kind_from_string (const char *str)
     122             : {
     123           9 :   if (0 == strcmp ("subscription",
     124             :                    str))
     125           7 :     return TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION;
     126           2 :   if (0 == strcmp ("discount",
     127             :                    str))
     128           2 :     return TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT;
     129           0 :   return TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID;
     130             : }
     131             : 
     132             : 
     133             : enum GNUNET_GenericReturnValue
     134          15 : TALER_MERCHANT_parse_choice_input (
     135             :   json_t *root,
     136             :   struct TALER_MERCHANT_ContractInput *input,
     137             :   size_t index,
     138             :   bool order)
     139             : {
     140             :   const char *ename;
     141             :   unsigned int eline;
     142             :   struct GNUNET_JSON_Specification ispec[] = {
     143          15 :     TALER_MERCHANT_json_spec_cit ("type",
     144             :                                   &input->type),
     145          15 :     GNUNET_JSON_spec_end ()
     146             :   };
     147             : 
     148          15 :   if (GNUNET_OK !=
     149          15 :       GNUNET_JSON_parse (root,
     150             :                          ispec,
     151             :                          &ename,
     152             :                          &eline))
     153             :   {
     154           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     155             :                 "Failed to parse %s at %u: %s\n",
     156             :                 ispec[eline].field,
     157             :                 eline,
     158             :                 ename);
     159           0 :     GNUNET_break_op (0);
     160           0 :     return GNUNET_SYSERR;
     161             :   }
     162             : 
     163          15 :   switch (input->type)
     164             :   {
     165           0 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
     166           0 :     GNUNET_break (0);
     167           0 :     break;
     168          15 :   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
     169             :     {
     170             :       struct GNUNET_JSON_Specification spec[] = {
     171          15 :         GNUNET_JSON_spec_string ("token_family_slug",
     172             :                                  &input->details.token.token_family_slug),
     173          15 :         GNUNET_JSON_spec_mark_optional (
     174             :           GNUNET_JSON_spec_uint32 ("count",
     175          15 :                                    &input->details.token.count),
     176             :           NULL),
     177          15 :         GNUNET_JSON_spec_end ()
     178             :       };
     179             : 
     180          15 :       if (GNUNET_OK !=
     181          15 :           GNUNET_JSON_parse (root,
     182             :                              spec,
     183             :                              &ename,
     184             :                              &eline))
     185             :       {
     186           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     187             :                     "Failed to parse %s at %u: %s\n",
     188             :                     spec[eline].field,
     189             :                     eline,
     190             :                     ename);
     191           0 :         GNUNET_break_op (0);
     192           0 :         return GNUNET_SYSERR;
     193             :       }
     194             : 
     195          15 :       return GNUNET_OK;
     196             :     }
     197             :   }
     198             : 
     199           0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     200             :               "Field 'type' invalid in input #%u\n",
     201             :               (unsigned int) index);
     202           0 :   GNUNET_break_op (0);
     203           0 :   return GNUNET_SYSERR;
     204             : }
     205             : 
     206             : 
     207             : enum GNUNET_GenericReturnValue
     208          16 : TALER_MERCHANT_parse_choice_output (
     209             :   json_t *root,
     210             :   struct TALER_MERCHANT_ContractOutput *output,
     211             :   size_t index,
     212             :   bool order)
     213             : {
     214             :   const char *ename;
     215             :   unsigned int eline;
     216             :   struct GNUNET_JSON_Specification ispec[] = {
     217          16 :     TALER_MERCHANT_json_spec_cot ("type",
     218             :                                   &output->type),
     219          16 :     GNUNET_JSON_spec_end ()
     220             :   };
     221             : 
     222          16 :   if (GNUNET_OK !=
     223          16 :       GNUNET_JSON_parse (root,
     224             :                          ispec,
     225             :                          &ename,
     226             :                          &eline))
     227             :   {
     228           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     229             :                 "Failed to parse %s at %u: %s\n",
     230             :                 ispec[eline].field,
     231             :                 eline,
     232             :                 ename);
     233           0 :     GNUNET_break_op (0);
     234           0 :     return GNUNET_SYSERR;
     235             :   }
     236             : 
     237          16 :   switch (output->type)
     238             :   {
     239           0 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     240           0 :     GNUNET_break (0);
     241           0 :     break;
     242          15 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     243             :     {
     244             :       struct GNUNET_JSON_Specification spec[] = {
     245          15 :         GNUNET_JSON_spec_string ("token_family_slug",
     246             :                                  &output->details.token.token_family_slug),
     247          15 :         GNUNET_JSON_spec_mark_optional (
     248             :           GNUNET_JSON_spec_uint ("count",
     249             :                                  &output->details.token.count),
     250             :           NULL),
     251          15 :         GNUNET_JSON_spec_mark_optional (
     252             :           GNUNET_JSON_spec_timestamp ("valid_at",
     253             :                                       &output->details.token.valid_at),
     254             :           NULL),
     255          15 :         (! order)
     256           7 :         ? GNUNET_JSON_spec_uint ("key_index",
     257             :                                  &output->details.token.key_index)
     258          15 :         : GNUNET_JSON_spec_end (),
     259          15 :         GNUNET_JSON_spec_end ()
     260             :       };
     261             : 
     262          15 :       if (GNUNET_OK !=
     263          15 :           GNUNET_JSON_parse (root,
     264             :                              spec,
     265             :                              &ename,
     266             :                              &eline))
     267             :       {
     268           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     269             :                     "Failed to parse %s at %u: %s\n",
     270             :                     spec[eline].field,
     271             :                     eline,
     272             :                     ename);
     273           0 :         GNUNET_break_op (0);
     274           0 :         return GNUNET_SYSERR;
     275             :       }
     276             : 
     277          15 :       return GNUNET_OK;
     278             :     }
     279           1 :   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     280             :     {
     281           1 :       const json_t *donau_urls = NULL;
     282             :       struct GNUNET_JSON_Specification spec[] = {
     283           1 :         GNUNET_JSON_spec_mark_optional (
     284             :           TALER_JSON_spec_amount_any ("amount",
     285             :                                       &output->details.donation_receipt.amount),
     286             :           NULL),
     287           1 :         (! order)
     288           1 :         ? GNUNET_JSON_spec_array_const ("donau_urls",
     289             :                                         &donau_urls)
     290           1 :         : GNUNET_JSON_spec_end (),
     291           1 :         GNUNET_JSON_spec_end ()
     292             :       };
     293             : 
     294           1 :       if (GNUNET_OK !=
     295           1 :           GNUNET_JSON_parse (root,
     296             :                              spec,
     297             :                              &ename,
     298             :                              &eline))
     299             :       {
     300           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     301             :                     "Failed to parse %s at %u: %s\n",
     302             :                     spec[eline].field,
     303             :                     eline,
     304             :                     ename);
     305           0 :         GNUNET_break_op (0);
     306           0 :         return GNUNET_SYSERR;
     307             :       }
     308             : 
     309           1 :       GNUNET_array_grow (output->details.donation_receipt.donau_urls,
     310             :                          output->details.donation_receipt.donau_urls_len,
     311             :                          json_array_size (donau_urls));
     312             : 
     313           1 :       for (unsigned int i = 0;
     314           4 :            i < output->details.donation_receipt.donau_urls_len;
     315           3 :            i++)
     316             :       {
     317             :         const json_t *jurl;
     318             : 
     319           3 :         jurl = json_array_get (donau_urls,
     320             :                                i);
     321           3 :         if (! json_is_string (jurl))
     322             :         {
     323           0 :           GNUNET_break_op (0);
     324           0 :           return GNUNET_SYSERR;
     325             :         }
     326             : 
     327           3 :         output->details.donation_receipt.donau_urls[i] =
     328           3 :           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             : 
     350             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     351             :  */
     352             : static enum GNUNET_GenericReturnValue
     353           8 : parse_choices (
     354             :   void *cls,
     355             :   json_t *root,
     356             :   struct GNUNET_JSON_Specification *ospec)
     357             : {
     358           8 :   struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr;
     359           8 :   unsigned int *choices_len = cls;
     360             : 
     361           8 :   if (! json_is_array (root))
     362             :   {
     363           0 :     GNUNET_break_op (0);
     364           0 :     return GNUNET_SYSERR;
     365             :   }
     366             : 
     367           8 :   GNUNET_array_grow (*choices,
     368             :                      *choices_len,
     369             :                      json_array_size (root));
     370             : 
     371          17 :   for (unsigned int i = 0; i < *choices_len; i++)
     372             :   {
     373           9 :     struct TALER_MERCHANT_ContractChoice *choice = &(*choices)[i];
     374             :     const json_t *jinputs;
     375             :     const json_t *joutputs;
     376             : 
     377             :     struct GNUNET_JSON_Specification spec[] = {
     378           9 :       TALER_JSON_spec_amount_any ("amount",
     379             :                                   &choice->amount),
     380           9 :       GNUNET_JSON_spec_mark_optional (
     381             :         GNUNET_JSON_spec_string_copy ("description",
     382             :                                       &choice->description),
     383             :         NULL),
     384           9 :       GNUNET_JSON_spec_mark_optional (
     385             :         GNUNET_JSON_spec_object_copy ("description_i18n",
     386             :                                       &choice->description_i18n),
     387             :         NULL),
     388           9 :       TALER_JSON_spec_amount_any ("max_fee",
     389             :                                   &choice->max_fee),
     390           9 :       GNUNET_JSON_spec_array_const ("inputs",
     391             :                                     &jinputs),
     392           9 :       GNUNET_JSON_spec_array_const ("outputs",
     393             :                                     &joutputs),
     394           9 :       GNUNET_JSON_spec_end ()
     395             :     };
     396             : 
     397             :     const char *ename;
     398             :     unsigned int eline;
     399             : 
     400           9 :     if (GNUNET_OK !=
     401           9 :         GNUNET_JSON_parse (json_array_get (root, i),
     402             :                            spec,
     403             :                            &ename,
     404             :                            &eline))
     405             :     {
     406           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     407             :                   "Failed to parse %s at %u: %s\n",
     408             :                   spec[eline].field,
     409             :                   eline,
     410             :                   ename);
     411           0 :       GNUNET_break_op (0);
     412           0 :       return GNUNET_SYSERR;
     413             :     }
     414             : 
     415             :     {
     416             :       const json_t *jinput;
     417             :       size_t idx;
     418          15 :       json_array_foreach ((json_t *) jinputs, idx, jinput)
     419             :       {
     420           6 :         struct TALER_MERCHANT_ContractInput input = {
     421             :           .details.token.count = 1
     422             :         };
     423             : 
     424           6 :         if (GNUNET_OK !=
     425           6 :             TALER_MERCHANT_parse_choice_input ((json_t *) jinput,
     426             :                                                &input,
     427             :                                                idx,
     428             :                                                false))
     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             : 
     443           6 :         GNUNET_array_append (choice->inputs,
     444             :                              choice->inputs_len,
     445             :                              input);
     446             :       }
     447             :     }
     448             : 
     449             :     {
     450             :       const json_t *joutput;
     451             :       size_t idx;
     452          17 :       json_array_foreach ((json_t *) joutputs, idx, joutput)
     453             :       {
     454           8 :         struct TALER_MERCHANT_ContractOutput output = {
     455             :           .details.token.count = 1
     456             :         };
     457             : 
     458           8 :         if (GNUNET_OK !=
     459           8 :             TALER_MERCHANT_parse_choice_output ((json_t *) joutput,
     460             :                                                 &output,
     461             :                                                 idx,
     462             :                                                 false))
     463           0 :           return GNUNET_SYSERR;
     464             : 
     465           8 :         switch (output.type)
     466             :         {
     467           0 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     468           0 :           GNUNET_break_op (0);
     469           0 :           return GNUNET_SYSERR;
     470           7 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     471             :           /* Ignore output tokens with 'count' field set to 0 */
     472           7 :           if (0 == output.details.token.count)
     473           0 :             continue;
     474           7 :           break;
     475           1 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     476           1 :           break;
     477             :         }
     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             : /**
     516             :  * Free all the fields in the given @a choice, but not @a choice itself, since
     517             :  * it is normally part of an array.
     518             :  *
     519             :  * @param[in] choice contract terms choice to free
     520             :  */
     521             : static void
     522           9 : contract_choice_free (
     523             :   struct TALER_MERCHANT_ContractChoice *choice)
     524             : {
     525          17 :   for (unsigned int i = 0; i < choice->outputs_len; i++)
     526             :   {
     527           8 :     struct TALER_MERCHANT_ContractOutput *output = &choice->outputs[i];
     528             : 
     529           8 :     GNUNET_free (output->details.coin.exchange_url);
     530             :   }
     531           9 :   GNUNET_free (choice->description);
     532           9 :   if (NULL != choice->description_i18n)
     533             :   {
     534           1 :     json_decref (choice->description_i18n);
     535           1 :     choice->description_i18n = NULL;
     536             :   }
     537           9 :   GNUNET_free (choice->inputs);
     538           9 :   GNUNET_free (choice->outputs);
     539           9 : }
     540             : 
     541             : 
     542             : /**
     543             :  * Parse token details of the given JSON contract token family.
     544             :  *
     545             :  * @param cls closure, unused parameter
     546             :  * @param root the JSON object representing data
     547             :  * @param[out] ospec where to write the data
     548             :  * @return #GNUNET_OK upon successful parsing; @GNUNET_SYSERR upon error
     549             :  */
     550             : static enum GNUNET_GenericReturnValue
     551           9 : parse_token_details (void *cls,
     552             :                      json_t *root,
     553             :                      struct GNUNET_JSON_Specification *ospec)
     554             : {
     555           9 :   struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
     556             :   const char *class;
     557             :   const char *ename;
     558             :   unsigned int eline;
     559             :   struct GNUNET_JSON_Specification ispec[] = {
     560           9 :     GNUNET_JSON_spec_string ("class",
     561             :                              &class),
     562           9 :     GNUNET_JSON_spec_end ()
     563             :   };
     564             : 
     565             :   (void) cls;
     566           9 :   if (GNUNET_OK !=
     567           9 :       GNUNET_JSON_parse (root,
     568             :                          ispec,
     569             :                          &ename,
     570             :                          &eline))
     571             :   {
     572           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     573             :                 "Failed to parse %s at %u: %s\n",
     574             :                 ispec[eline].field,
     575             :                 eline,
     576             :                 ename);
     577           0 :     GNUNET_break_op (0);
     578           0 :     return GNUNET_SYSERR;
     579             :   }
     580             : 
     581           9 :   family->kind = contract_token_kind_from_string (class);
     582             : 
     583           9 :   switch (family->kind)
     584             :   {
     585           0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     586           0 :     break;
     587           7 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     588             :     {
     589             :       const json_t *trusted_domains;
     590             :       struct GNUNET_JSON_Specification spec[] = {
     591           7 :         GNUNET_JSON_spec_array_const ("trusted_domains",
     592             :                                       &trusted_domains),
     593           7 :         GNUNET_JSON_spec_end ()
     594             :       };
     595             : 
     596           7 :       if (GNUNET_OK !=
     597           7 :           GNUNET_JSON_parse (root,
     598             :                              spec,
     599             :                              &ename,
     600             :                              &eline))
     601             :       {
     602           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     603             :                     "Failed to parse %s at %u: %s\n",
     604             :                     spec[eline].field,
     605             :                     eline,
     606             :                     ename);
     607           0 :         GNUNET_break_op (0);
     608           0 :         return GNUNET_SYSERR;
     609             :       }
     610             : 
     611           7 :       GNUNET_array_grow (family->details.subscription.trusted_domains,
     612             :                          family->details.subscription.trusted_domains_len,
     613             :                          json_array_size (trusted_domains));
     614             : 
     615           7 :       for (unsigned int i = 0;
     616          10 :            i < family->details.subscription.trusted_domains_len;
     617           3 :            i++)
     618             :       {
     619             :         const json_t *jdomain;
     620           3 :         jdomain = json_array_get (trusted_domains, i);
     621             : 
     622           3 :         if (! json_is_string (jdomain))
     623             :         {
     624           0 :           GNUNET_break_op (0);
     625           0 :           return GNUNET_SYSERR;
     626             :         }
     627             : 
     628           3 :         family->details.subscription.trusted_domains[i] =
     629           3 :           GNUNET_strdup (json_string_value (jdomain));
     630             :       }
     631             : 
     632           7 :       return GNUNET_OK;
     633             :     }
     634           2 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     635             :     {
     636             :       const json_t *expected_domains;
     637             :       struct GNUNET_JSON_Specification spec[] = {
     638           2 :         GNUNET_JSON_spec_array_const ("expected_domains",
     639             :                                       &expected_domains),
     640           2 :         GNUNET_JSON_spec_end ()
     641             :       };
     642             : 
     643           2 :       if (GNUNET_OK !=
     644           2 :           GNUNET_JSON_parse (root,
     645             :                              spec,
     646             :                              &ename,
     647             :                              &eline))
     648             :       {
     649           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     650             :                     "Failed to parse %s at %u: %s\n",
     651             :                     spec[eline].field,
     652             :                     eline,
     653             :                     ename);
     654           0 :         GNUNET_break_op (0);
     655           0 :         return GNUNET_SYSERR;
     656             :       }
     657             : 
     658           2 :       GNUNET_array_grow (family->details.discount.expected_domains,
     659             :                          family->details.discount.expected_domains_len,
     660             :                          json_array_size (expected_domains));
     661             : 
     662           2 :       for (unsigned int i = 0;
     663           5 :            i < family->details.discount.expected_domains_len;
     664           3 :            i++)
     665             :       {
     666             :         const json_t *jdomain;
     667             : 
     668           3 :         jdomain = json_array_get (expected_domains,
     669             :                                   i);
     670           3 :         if (! json_is_string (jdomain))
     671             :         {
     672           0 :           GNUNET_break_op (0);
     673           0 :           return GNUNET_SYSERR;
     674             :         }
     675             : 
     676           3 :         family->details.discount.expected_domains[i] =
     677           3 :           GNUNET_strdup (json_string_value (jdomain));
     678             :       }
     679             : 
     680           2 :       return GNUNET_OK;
     681             :     }
     682             :   }
     683             : 
     684           0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     685             :               "Field 'type' invalid in token family\n");
     686           0 :   GNUNET_break_op (0);
     687           0 :   return GNUNET_SYSERR;
     688             : }
     689             : 
     690             : 
     691             : /**
     692             :  * Provide specification to parse given JSON object to contract token details
     693             :  * in the contract token family. All fields from @a family are copied.
     694             :  *
     695             :  * @param name name of the token details field in the JSON
     696             :  * @param[out] token family where the token details have to be written
     697             :  */
     698             : static struct GNUNET_JSON_Specification
     699           9 : spec_token_details (const char *name,
     700             :                     struct TALER_MERCHANT_ContractTokenFamily *family)
     701             : {
     702           9 :   struct GNUNET_JSON_Specification ret = {
     703             :     .parser = &parse_token_details,
     704             :     .field = name,
     705             :     .ptr = family,
     706             :   };
     707             : 
     708           9 :   return ret;
     709             : }
     710             : 
     711             : 
     712             : /**
     713             :  * Parse given JSON object to token families array.
     714             :  *
     715             :  * @param cls closure, pointer to array length
     716             :  * @param root the json object representing the token families. The keys are
     717             :  *             the token family slugs.
     718             :  * @param[out] ospec where to write the data
     719             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     720             :  */
     721             : static enum GNUNET_GenericReturnValue
     722           8 : parse_token_families (void *cls,
     723             :                       json_t *root,
     724             :                       struct GNUNET_JSON_Specification *ospec)
     725             : {
     726           8 :   struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
     727           8 :   unsigned int *families_len = cls;
     728             :   json_t *jfamily;
     729             :   const char *slug;
     730             : 
     731           8 :   if (! json_is_object (root))
     732             :   {
     733           0 :     GNUNET_break_op (0);
     734           0 :     return GNUNET_SYSERR;
     735             :   }
     736             : 
     737          17 :   json_object_foreach (root, slug, jfamily)
     738             :   {
     739             :     const json_t *keys;
     740          18 :     struct TALER_MERCHANT_ContractTokenFamily family = {
     741           9 :       .slug = GNUNET_strdup (slug)
     742             :     };
     743             :     struct GNUNET_JSON_Specification spec[] = {
     744           9 :       GNUNET_JSON_spec_string_copy ("name",
     745             :                                     &family.name),
     746           9 :       GNUNET_JSON_spec_string_copy ("description",
     747             :                                     &family.description),
     748           9 :       GNUNET_JSON_spec_object_copy ("description_i18n",
     749             :                                     &family.description_i18n),
     750           9 :       GNUNET_JSON_spec_array_const ("keys",
     751             :                                     &keys),
     752           9 :       spec_token_details ("details",
     753             :                           &family),
     754           9 :       GNUNET_JSON_spec_bool ("critical",
     755             :                              &family.critical),
     756           9 :       GNUNET_JSON_spec_end ()
     757             :     };
     758             :     const char *error_name;
     759             :     unsigned int error_line;
     760             : 
     761           9 :     if (GNUNET_OK !=
     762           9 :         GNUNET_JSON_parse (jfamily,
     763             :                            spec,
     764             :                            &error_name,
     765             :                            &error_line))
     766             :     {
     767           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     768             :                   "Failed to parse %s at %u: %s\n",
     769             :                   spec[error_line].field,
     770             :                   error_line,
     771             :                   error_name);
     772           0 :       GNUNET_break_op (0);
     773           0 :       return GNUNET_SYSERR;
     774             :     }
     775             : 
     776           9 :     GNUNET_array_grow (family.keys,
     777             :                        family.keys_len,
     778             :                        json_array_size (keys));
     779             : 
     780          17 :     for (unsigned int i = 0; i<family.keys_len; i++)
     781             :     {
     782           8 :       struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
     783             :       struct GNUNET_JSON_Specification key_spec[] = {
     784           8 :         TALER_JSON_spec_token_pub (
     785             :           NULL,
     786             :           &key->pub),
     787           8 :         GNUNET_JSON_spec_timestamp (
     788             :           "signature_validity_start",
     789             :           &key->valid_after),
     790           8 :         GNUNET_JSON_spec_timestamp (
     791             :           "signature_validity_end",
     792             :           &key->valid_before),
     793           8 :         GNUNET_JSON_spec_end ()
     794             :       };
     795             :       const char *ierror_name;
     796             :       unsigned int ierror_line;
     797             : 
     798           8 :       if (GNUNET_OK !=
     799           8 :           GNUNET_JSON_parse (json_array_get (keys,
     800             :                                              i),
     801             :                              key_spec,
     802             :                              &ierror_name,
     803             :                              &ierror_line))
     804             :       {
     805           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     806             :                     "Failed to parse %s at %u: %s\n",
     807             :                     key_spec[ierror_line].field,
     808             :                     ierror_line,
     809             :                     ierror_name);
     810           0 :         GNUNET_break_op (0);
     811           0 :         return GNUNET_SYSERR;
     812             :       }
     813             :     }
     814             : 
     815           9 :     GNUNET_array_append (*families,
     816             :                          *families_len,
     817             :                          family);
     818             :   }
     819             : 
     820           8 :   return GNUNET_OK;
     821             : }
     822             : 
     823             : 
     824             : /**
     825             :  * Provide specification to parse given JSON array to token families in the
     826             :  * contract terms. All fields from @a families items are copied.
     827             :  *
     828             :  * @param name name of the token families field in the JSON
     829             :  * @param[out] families where the token families array has to be written
     830             :  * @param[out] families_len length of the @a families array
     831             :  */
     832             : static struct GNUNET_JSON_Specification
     833           8 : spec_token_families (
     834             :   const char *name,
     835             :   struct TALER_MERCHANT_ContractTokenFamily **families,
     836             :   unsigned int *families_len)
     837             : {
     838           8 :   struct GNUNET_JSON_Specification ret = {
     839             :     .cls = (void *) families_len,
     840             :     .parser = &parse_token_families,
     841             :     .field = name,
     842             :     .ptr = families,
     843             :   };
     844             : 
     845           8 :   return ret;
     846             : }
     847             : 
     848             : 
     849             : enum GNUNET_GenericReturnValue
     850           2 : TALER_MERCHANT_find_token_family_key (
     851             :   const char *slug,
     852             :   struct GNUNET_TIME_Timestamp valid_after,
     853             :   const struct TALER_MERCHANT_ContractTokenFamily *families,
     854             :   unsigned int families_len,
     855             :   struct TALER_MERCHANT_ContractTokenFamily *family,
     856             :   struct TALER_MERCHANT_ContractTokenFamilyKey *key)
     857             : {
     858           3 :   for (unsigned int i = 0; i < families_len; i++)
     859             :   {
     860           3 :     const struct TALER_MERCHANT_ContractTokenFamily *fami
     861           3 :       = &families[i];
     862             : 
     863           3 :     if (0 != strcmp (fami->slug,
     864             :                      slug))
     865           1 :       continue;
     866           2 :     if (NULL != family)
     867           2 :       *family = *fami;
     868           2 :     for (unsigned int k = 0; k < fami->keys_len; k++)
     869             :     {
     870           2 :       struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
     871             : 
     872           2 :       if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
     873             :                                      ==,
     874             :                                      valid_after))
     875             :       {
     876           2 :         if (NULL != key)
     877           2 :           *key = *ki;
     878           2 :         return GNUNET_OK;
     879             :       }
     880             :     }
     881             :     /* matching family found, but no key. */
     882           0 :     return GNUNET_NO;
     883             :   }
     884             : 
     885             :   /* no matching family found */
     886           0 :   return GNUNET_SYSERR;
     887             : }
     888             : 
     889             : 
     890             : /**
     891             :  * Free all the fields in the given @a family, but not @a family itself, since
     892             :  * it is normally part of an array.
     893             :  *
     894             :  * @param[in] family contract token family to free
     895             :  */
     896             : static void
     897           9 : contract_token_family_free (
     898             :   struct TALER_MERCHANT_ContractTokenFamily *family)
     899             : {
     900           9 :   GNUNET_free (family->slug);
     901           9 :   GNUNET_free (family->name);
     902           9 :   GNUNET_free (family->description);
     903           9 :   if (NULL != family->description_i18n)
     904             :   {
     905           9 :     json_decref (family->description_i18n);
     906           9 :     family->description_i18n = NULL;
     907             :   }
     908          17 :   for (unsigned int i = 0; i < family->keys_len; i++)
     909           8 :     TALER_token_issue_pub_free (&family->keys[i].pub);
     910           9 :   GNUNET_free (family->keys);
     911             : 
     912           9 :   switch (family->kind)
     913             :   {
     914           0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     915           0 :     break;
     916           2 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     917           5 :     for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
     918           3 :          i++)
     919           3 :       GNUNET_free (family->details.discount.expected_domains[i]);
     920           2 :     break;
     921           7 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     922          10 :     for (unsigned int i = 0; i < family->details.subscription.
     923           3 :          trusted_domains_len; i++)
     924           3 :       GNUNET_free (family->details.subscription.trusted_domains[i]);
     925           7 :     break;
     926             :   }
     927           9 : }
     928             : 
     929             : 
     930             : /**
     931             :  * Parse contract version of given JSON contract terms.
     932             :  *
     933             :  * @param cls closure, unused parameter
     934             :  * @param root the JSON object representing data
     935             :  * @param[out] spec where to write the data
     936             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     937             :  */
     938             : static enum GNUNET_GenericReturnValue
     939         152 : parse_contract_version (void *cls,
     940             :                         json_t *root,
     941             :                         struct GNUNET_JSON_Specification *spec)
     942             : {
     943         152 :   enum TALER_MERCHANT_ContractVersion *res
     944             :     = (enum TALER_MERCHANT_ContractVersion *) spec->ptr;
     945             : 
     946             :   (void) cls;
     947         152 :   if (json_is_integer (root))
     948             :   {
     949         152 :     json_int_t version = json_integer_value (root);
     950             : 
     951         152 :     switch (version)
     952             :     {
     953         144 :     case 0:
     954         144 :       *res = TALER_MERCHANT_CONTRACT_VERSION_0;
     955         144 :       return GNUNET_OK;
     956           8 :     case 1:
     957           8 :       *res = TALER_MERCHANT_CONTRACT_VERSION_1;
     958           8 :       return GNUNET_OK;
     959             :     }
     960             : 
     961           0 :     GNUNET_break_op (0);
     962           0 :     return GNUNET_SYSERR;
     963             :   }
     964             : 
     965           0 :   if (json_is_null (root))
     966             :   {
     967           0 :     *res = TALER_MERCHANT_CONTRACT_VERSION_0;
     968           0 :     return GNUNET_OK;
     969             :   }
     970             : 
     971           0 :   GNUNET_break_op (0);
     972           0 :   return GNUNET_SYSERR;
     973             : }
     974             : 
     975             : 
     976             : /**
     977             :  * Create JSON specification to parse a merchant contract
     978             :  * version.
     979             :  *
     980             :  * @param name name of the field
     981             :  * @param[out] version where to write the contract version
     982             :  * @return JSON specification object
     983             :  */
     984             : static struct GNUNET_JSON_Specification
     985         152 : spec_contract_version (
     986             :   const char *name,
     987             :   enum TALER_MERCHANT_ContractVersion *version)
     988             : {
     989         152 :   struct GNUNET_JSON_Specification ret = {
     990             :     .parser = &parse_contract_version,
     991             :     .field = name,
     992             :     .ptr = version
     993             :   };
     994             : 
     995         152 :   *version = TALER_MERCHANT_CONTRACT_VERSION_0;
     996         152 :   return ret;
     997             : }
     998             : 
     999             : 
    1000             : /**
    1001             :  * Parse v0-specific fields of @a input JSON into @a contract.
    1002             :  *
    1003             :  * @param[in] input the JSON contract terms
    1004             :  * @param[out] contract where to write the data
    1005             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    1006             :  */
    1007             : static enum GNUNET_GenericReturnValue
    1008         144 : parse_contract_v0 (
    1009             :   json_t *input,
    1010             :   struct TALER_MERCHANT_Contract *contract)
    1011             : {
    1012             :   struct GNUNET_JSON_Specification espec[] = {
    1013         144 :     TALER_JSON_spec_amount_any ("amount",
    1014             :                                 &contract->details.v0.brutto),
    1015         144 :     TALER_JSON_spec_amount_any ("max_fee",
    1016             :                                 &contract->details.v0.max_fee),
    1017         144 :     GNUNET_JSON_spec_end ()
    1018             :   };
    1019             :   enum GNUNET_GenericReturnValue res;
    1020             :   const char *ename;
    1021             :   unsigned int eline;
    1022             : 
    1023         144 :   res = GNUNET_JSON_parse (input,
    1024             :                            espec,
    1025             :                            &ename,
    1026             :                            &eline);
    1027         144 :   if (GNUNET_OK != res)
    1028             :   {
    1029           0 :     GNUNET_break (0);
    1030           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1031             :                 "Failed to parse contract v0 at field %s\n",
    1032             :                 ename);
    1033           0 :     return GNUNET_SYSERR;
    1034             :   }
    1035             : 
    1036         144 :   if (GNUNET_OK !=
    1037         144 :       TALER_amount_cmp_currency (&contract->details.v0.max_fee,
    1038         144 :                                  &contract->details.v0.brutto))
    1039             :   {
    1040           0 :     GNUNET_break (0);
    1041           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1042             :                 "'max_fee' in database does not match currency of contract price");
    1043           0 :     return GNUNET_SYSERR;
    1044             :   }
    1045             : 
    1046         144 :   return res;
    1047             : }
    1048             : 
    1049             : 
    1050             : /**
    1051             :  * Parse v1-specific fields of @a input JSON into @a contract.
    1052             :  *
    1053             :  * @param[in] input the JSON contract terms
    1054             :  * @param[out] contract where to write the data
    1055             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    1056             :  */
    1057             : static enum GNUNET_GenericReturnValue
    1058           8 : parse_contract_v1 (
    1059             :   json_t *input,
    1060             :   struct TALER_MERCHANT_Contract *contract)
    1061             : {
    1062             :   struct GNUNET_JSON_Specification espec[] = {
    1063           8 :     spec_choices ("choices",
    1064             :                   &contract->details.v1.choices,
    1065             :                   &contract->details.v1.choices_len),
    1066           8 :     spec_token_families ("token_families",
    1067             :                          &contract->details.v1.token_authorities,
    1068             :                          &contract->details.v1.
    1069             :                          token_authorities_len),
    1070           8 :     GNUNET_JSON_spec_end ()
    1071             :   };
    1072             : 
    1073             :   enum GNUNET_GenericReturnValue res;
    1074             :   const char *ename;
    1075             :   unsigned int eline;
    1076             : 
    1077           8 :   res = GNUNET_JSON_parse (input,
    1078             :                            espec,
    1079             :                            &ename,
    1080             :                            &eline);
    1081             : 
    1082           8 :   if (GNUNET_OK != res)
    1083             :   {
    1084           0 :     GNUNET_break (0);
    1085           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1086             :                 "Failed to parse contract v1 at field %s\n",
    1087             :                 ename);
    1088           0 :     return GNUNET_SYSERR;
    1089             :   }
    1090             : 
    1091           8 :   return res;
    1092             : }
    1093             : 
    1094             : 
    1095             : struct TALER_MERCHANT_Contract *
    1096         152 : TALER_MERCHANT_contract_parse (json_t *input,
    1097             :                                bool nonce_optional)
    1098             : {
    1099             :   struct TALER_MERCHANT_Contract *contract
    1100         152 :     = GNUNET_new (struct TALER_MERCHANT_Contract);
    1101             :   struct GNUNET_JSON_Specification espec[] = {
    1102         152 :     spec_contract_version ("version",
    1103             :                            &contract->version),
    1104         152 :     GNUNET_JSON_spec_string_copy ("summary",
    1105             :                                   &contract->summary),
    1106             :     /* FIXME: do i18n_str validation in the future */
    1107         152 :     GNUNET_JSON_spec_mark_optional (
    1108             :       GNUNET_JSON_spec_object_copy ("summary_i18n",
    1109             :                                     &contract->summary_i18n),
    1110             :       NULL),
    1111         152 :     GNUNET_JSON_spec_string_copy ("order_id",
    1112             :                                   &contract->order_id),
    1113         152 :     GNUNET_JSON_spec_mark_optional (
    1114             :       GNUNET_JSON_spec_string_copy ("public_reorder_url",
    1115             :                                     &contract->public_reorder_url),
    1116             :       NULL),
    1117         152 :     GNUNET_JSON_spec_mark_optional (
    1118             :       GNUNET_JSON_spec_string_copy ("fulfillment_url",
    1119             :                                     &contract->fulfillment_url),
    1120             :       NULL),
    1121         152 :     GNUNET_JSON_spec_mark_optional (
    1122             :       GNUNET_JSON_spec_string_copy ("fulfillment_message",
    1123             :                                     &contract->fulfillment_message),
    1124             :       NULL),
    1125         152 :     GNUNET_JSON_spec_mark_optional (
    1126             :       GNUNET_JSON_spec_object_copy ("fulfillment_message_i18n",
    1127             :                                     &contract->fulfillment_message_i18n),
    1128             :       NULL),
    1129         152 :     GNUNET_JSON_spec_array_copy ("products",
    1130             :                                  &contract->products),
    1131         152 :     GNUNET_JSON_spec_timestamp ("timestamp",
    1132             :                                 &contract->timestamp),
    1133         152 :     GNUNET_JSON_spec_timestamp ("refund_deadline",
    1134             :                                 &contract->refund_deadline),
    1135         152 :     GNUNET_JSON_spec_timestamp ("pay_deadline",
    1136             :                                 &contract->pay_deadline),
    1137         152 :     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    1138             :                                 &contract->wire_deadline),
    1139         152 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
    1140             :                                  &contract->merchant_pub),
    1141         152 :     GNUNET_JSON_spec_string_copy ("merchant_base_url",
    1142             :                                   &contract->merchant_base_url),
    1143         152 :     spec_merchant_details ("merchant",
    1144             :                            contract),
    1145         152 :     GNUNET_JSON_spec_fixed_auto ("h_wire",
    1146             :                                  &contract->h_wire),
    1147         152 :     GNUNET_JSON_spec_string_copy ("wire_method",
    1148             :                                   &contract->wire_method),
    1149         152 :     GNUNET_JSON_spec_array_copy ("exchanges",
    1150             :                                  &contract->exchanges),
    1151         152 :     GNUNET_JSON_spec_mark_optional (
    1152             :       GNUNET_JSON_spec_object_copy ("delivery_location",
    1153             :                                     &contract->delivery_location),
    1154             :       NULL),
    1155         152 :     GNUNET_JSON_spec_mark_optional (
    1156             :       GNUNET_JSON_spec_timestamp ("delivery_date",
    1157             :                                   &contract->delivery_date),
    1158             :       NULL),
    1159             :     (nonce_optional)
    1160         150 :     ? GNUNET_JSON_spec_mark_optional (
    1161             :       GNUNET_JSON_spec_string_copy ("nonce",
    1162             :                                     &contract->nonce),
    1163             :       NULL)
    1164         152 :     : GNUNET_JSON_spec_string_copy ("nonce",
    1165             :                                     &contract->nonce),
    1166         152 :     GNUNET_JSON_spec_mark_optional (
    1167             :       GNUNET_JSON_spec_relative_time ("auto_refund",
    1168             :                                       &contract->auto_refund),
    1169             :       NULL),
    1170         152 :     GNUNET_JSON_spec_mark_optional (
    1171             :       GNUNET_JSON_spec_object_copy ("extra",
    1172             :                                     &contract->extra),
    1173             :       NULL),
    1174         152 :     GNUNET_JSON_spec_mark_optional (
    1175             :       GNUNET_JSON_spec_uint8 ("minimum_age",
    1176             :                               &contract->minimum_age),
    1177             :       NULL),
    1178         152 :     GNUNET_JSON_spec_end ()
    1179             :   };
    1180             : 
    1181             :   enum GNUNET_GenericReturnValue res;
    1182             :   const char *ename;
    1183             :   unsigned int eline;
    1184             : 
    1185         152 :   GNUNET_assert (NULL != input);
    1186         152 :   res = GNUNET_JSON_parse (input,
    1187             :                            espec,
    1188             :                            &ename,
    1189             :                            &eline);
    1190         152 :   if (GNUNET_OK != res)
    1191             :   {
    1192           0 :     GNUNET_break (0);
    1193           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1194             :                 "Failed to parse contract at field %s\n",
    1195             :                 ename);
    1196           0 :     goto cleanup;
    1197             :   }
    1198             : 
    1199         152 :   switch (contract->version)
    1200             :   {
    1201         144 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1202         144 :     res = parse_contract_v0 (input,
    1203             :                              contract);
    1204         144 :     break;
    1205           8 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1206           8 :     res = parse_contract_v1 (input,
    1207             :                              contract);
    1208           8 :     break;
    1209             :   }
    1210             : 
    1211         152 :   if (GNUNET_OK != res)
    1212             :   {
    1213           0 :     GNUNET_break (0);
    1214           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1215             :                 "Failed to parse contract\n");
    1216           0 :     goto cleanup;
    1217             :   }
    1218         152 :   return contract;
    1219             : 
    1220           0 : cleanup:
    1221           0 :   TALER_MERCHANT_contract_free (contract);
    1222           0 :   return NULL;
    1223             : }
    1224             : 
    1225             : 
    1226             : void
    1227         152 : TALER_MERCHANT_contract_free (
    1228             :   struct TALER_MERCHANT_Contract *contract)
    1229             : {
    1230         152 :   if (NULL == contract)
    1231           0 :     return;
    1232         152 :   GNUNET_free (contract->public_reorder_url);
    1233         152 :   GNUNET_free (contract->order_id);
    1234         152 :   GNUNET_free (contract->merchant_base_url);
    1235         152 :   GNUNET_free (contract->merchant.name);
    1236         152 :   GNUNET_free (contract->merchant.website);
    1237         152 :   GNUNET_free (contract->merchant.email);
    1238         152 :   GNUNET_free (contract->merchant.logo);
    1239         152 :   if (NULL != contract->merchant.address)
    1240             :   {
    1241         152 :     json_decref (contract->merchant.address);
    1242         152 :     contract->merchant.address = NULL;
    1243             :   }
    1244         152 :   if (NULL != contract->merchant.jurisdiction)
    1245             :   {
    1246         152 :     json_decref (contract->merchant.jurisdiction);
    1247         152 :     contract->merchant.jurisdiction = NULL;
    1248             :   }
    1249         152 :   GNUNET_free (contract->summary);
    1250         152 :   GNUNET_free (contract->fulfillment_url);
    1251         152 :   GNUNET_free (contract->fulfillment_message);
    1252         152 :   if (NULL != contract->fulfillment_message_i18n)
    1253             :   {
    1254           2 :     json_decref (contract->fulfillment_message_i18n);
    1255           2 :     contract->fulfillment_message_i18n = NULL;
    1256             :   }
    1257         152 :   if (NULL != contract->products)
    1258             :   {
    1259         152 :     json_decref (contract->products);
    1260         152 :     contract->products = NULL;
    1261             :   }
    1262         152 :   GNUNET_free (contract->wire_method);
    1263         152 :   if (NULL != contract->exchanges)
    1264             :   {
    1265         152 :     json_decref (contract->exchanges);
    1266         152 :     contract->exchanges = NULL;
    1267             :   }
    1268         152 :   if (NULL != contract->delivery_location)
    1269             :   {
    1270           2 :     json_decref (contract->delivery_location);
    1271           2 :     contract->delivery_location = NULL;
    1272             :   }
    1273         152 :   GNUNET_free (contract->nonce);
    1274         152 :   if (NULL != contract->extra)
    1275             :   {
    1276           2 :     json_decref (contract->extra);
    1277           2 :     contract->extra = NULL;
    1278             :   }
    1279             : 
    1280         152 :   switch (contract->version)
    1281             :   {
    1282         144 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1283         144 :     break;
    1284           8 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1285           8 :     for (unsigned int i = 0;
    1286          17 :          i < contract->details.v1.choices_len;
    1287           9 :          i++)
    1288           9 :       contract_choice_free (&contract->details.v1.choices[i]);
    1289           8 :     GNUNET_free (contract->details.v1.choices);
    1290           8 :     for (unsigned int i = 0;
    1291          17 :          i < contract->details.v1.token_authorities_len;
    1292           9 :          i++)
    1293           9 :       contract_token_family_free (&contract->details.v1.token_authorities[i]);
    1294           8 :     GNUNET_free (contract->details.v1.token_authorities);
    1295           8 :     break;
    1296             :   }
    1297         152 :   GNUNET_free (contract);
    1298             : }

Generated by: LCOV version 1.16