LCOV - code coverage report
Current view: top level - util - contract_parse.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 343 447 76.7 %
Date: 2025-06-23 16:22:09 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         139 : parse_merchant_details (void *cls,
      42             :                         json_t *root,
      43             :                         struct GNUNET_JSON_Specification *ospec)
      44             : {
      45         139 :   struct TALER_MERCHANT_Contract *contract = ospec->ptr;
      46             :   struct GNUNET_JSON_Specification spec[] = {
      47         139 :     GNUNET_JSON_spec_string_copy ("name",
      48             :                                   &contract->merchant.name),
      49         139 :     GNUNET_JSON_spec_mark_optional (
      50             :       GNUNET_JSON_spec_string_copy ("email",
      51             :                                     &contract->merchant.email),
      52             :       NULL),
      53         139 :     GNUNET_JSON_spec_mark_optional (
      54             :       GNUNET_JSON_spec_string_copy ("website",
      55             :                                     &contract->merchant.website),
      56             :       NULL),
      57         139 :     GNUNET_JSON_spec_mark_optional (
      58             :       GNUNET_JSON_spec_string_copy ("logo",
      59             :                                     &contract->merchant.logo),
      60             :       NULL),
      61         139 :     GNUNET_JSON_spec_mark_optional (
      62             :       GNUNET_JSON_spec_object_copy ("address",
      63             :                                     &contract->merchant.address),
      64             :       NULL),
      65         139 :     GNUNET_JSON_spec_mark_optional (
      66             :       GNUNET_JSON_spec_object_copy ("jurisdiction",
      67             :                                     &contract->merchant.jurisdiction),
      68             :       NULL),
      69         139 :     GNUNET_JSON_spec_end ()
      70             :   };
      71             :   const char *error_name;
      72             :   unsigned int error_line;
      73             : 
      74             :   (void) cls;
      75         139 :   if (GNUNET_OK !=
      76         139 :       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         139 :   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         139 : spec_merchant_details (const char *name,
     102             :                        struct TALER_MERCHANT_Contract *contract)
     103             : {
     104         139 :   struct GNUNET_JSON_Specification ret = {
     105             :     .parser = &parse_merchant_details,
     106             :     .field = name,
     107             :     .ptr = contract,
     108             :   };
     109             : 
     110         139 :   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 :       TALER_JSON_spec_amount_any ("max_fee",
     381             :                                   &choice->max_fee),
     382           9 :       GNUNET_JSON_spec_array_const ("inputs",
     383             :                                     &jinputs),
     384           9 :       GNUNET_JSON_spec_array_const ("outputs",
     385             :                                     &joutputs),
     386           9 :       GNUNET_JSON_spec_end ()
     387             :     };
     388             : 
     389             :     const char *ename;
     390             :     unsigned int eline;
     391             : 
     392           9 :     if (GNUNET_OK !=
     393           9 :         GNUNET_JSON_parse (json_array_get (root, i),
     394             :                            spec,
     395             :                            &ename,
     396             :                            &eline))
     397             :     {
     398           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     399             :                   "Failed to parse %s at %u: %s\n",
     400             :                   spec[eline].field,
     401             :                   eline,
     402             :                   ename);
     403           0 :       GNUNET_break_op (0);
     404           0 :       return GNUNET_SYSERR;
     405             :     }
     406             : 
     407             :     {
     408             :       const json_t *jinput;
     409             :       size_t idx;
     410          15 :       json_array_foreach ((json_t *) jinputs, idx, jinput)
     411             :       {
     412           6 :         struct TALER_MERCHANT_ContractInput input = {
     413             :           .details.token.count = 1
     414             :         };
     415             : 
     416           6 :         if (GNUNET_OK !=
     417           6 :             TALER_MERCHANT_parse_choice_input ((json_t *) jinput,
     418             :                                                &input,
     419             :                                                idx,
     420             :                                                false))
     421           0 :           return GNUNET_SYSERR;
     422             : 
     423           6 :         switch (input.type)
     424             :         {
     425           0 :         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
     426           0 :           GNUNET_break_op (0);
     427           0 :           return GNUNET_SYSERR;
     428           6 :         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
     429             :           /* Ignore inputs tokens with 'count' field set to 0 */
     430           6 :           if (0 == input.details.token.count)
     431           0 :             continue;
     432           6 :           break;
     433             :         }
     434             : 
     435           6 :         GNUNET_array_append (choice->inputs,
     436             :                              choice->inputs_len,
     437             :                              input);
     438             :       }
     439             :     }
     440             : 
     441             :     {
     442             :       const json_t *joutput;
     443             :       size_t idx;
     444          17 :       json_array_foreach ((json_t *) joutputs, idx, joutput)
     445             :       {
     446           8 :         struct TALER_MERCHANT_ContractOutput output = {
     447             :           .details.token.count = 1
     448             :         };
     449             : 
     450           8 :         if (GNUNET_OK !=
     451           8 :             TALER_MERCHANT_parse_choice_output ((json_t *) joutput,
     452             :                                                 &output,
     453             :                                                 idx,
     454             :                                                 false))
     455           0 :           return GNUNET_SYSERR;
     456             : 
     457           8 :         switch (output.type)
     458             :         {
     459           0 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
     460           0 :           GNUNET_break_op (0);
     461           0 :           return GNUNET_SYSERR;
     462           7 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
     463             :           /* Ignore output tokens with 'count' field set to 0 */
     464           7 :           if (0 == output.details.token.count)
     465           0 :             continue;
     466           7 :           break;
     467           1 :         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
     468           1 :           break;
     469             :         }
     470             : 
     471           8 :         GNUNET_array_append (choice->outputs,
     472             :                              choice->outputs_len,
     473             :                              output);
     474             :       }
     475             :     }
     476             :   }
     477             : 
     478           8 :   return GNUNET_OK;
     479             : }
     480             : 
     481             : 
     482             : /**
     483             :  * Provide specification to parse given JSON array to contract terms
     484             :  * choices. All fields from @a choices elements are copied.
     485             :  *
     486             :  * @param name name of the choices field in the JSON
     487             :  * @param[out] choices where the contract choices array has to be written
     488             :  * @param[out] choices_len length of the @a choices array
     489             :  */
     490             : static struct GNUNET_JSON_Specification
     491           8 : spec_choices (
     492             :   const char *name,
     493             :   struct TALER_MERCHANT_ContractChoice **choices,
     494             :   unsigned int *choices_len)
     495             : {
     496           8 :   struct GNUNET_JSON_Specification ret = {
     497             :     .cls = (void *) choices_len,
     498             :     .parser = &parse_choices,
     499             :     .field = name,
     500             :     .ptr = choices,
     501             :   };
     502             : 
     503           8 :   return ret;
     504             : }
     505             : 
     506             : 
     507             : /**
     508             :  * Free all the fields in the given @a choice, but not @a choice itself, since
     509             :  * it is normally part of an array.
     510             :  *
     511             :  * @param[in] choice contract terms choice to free
     512             :  */
     513             : static void
     514           9 : contract_choice_free (
     515             :   struct TALER_MERCHANT_ContractChoice *choice)
     516             : {
     517          17 :   for (unsigned int i = 0; i < choice->outputs_len; i++)
     518             :   {
     519           8 :     struct TALER_MERCHANT_ContractOutput *output = &choice->outputs[i];
     520             : 
     521           8 :     GNUNET_free (output->details.coin.exchange_url);
     522             :   }
     523           9 :   GNUNET_free (choice->inputs);
     524           9 :   GNUNET_free (choice->outputs);
     525           9 : }
     526             : 
     527             : 
     528             : /**
     529             :  * Parse token details of the given JSON contract token family.
     530             :  *
     531             :  * @param cls closure, unused parameter
     532             :  * @param root the JSON object representing data
     533             :  * @param[out] ospec where to write the data
     534             :  * @return #GNUNET_OK upon successful parsing; @GNUNET_SYSERR upon error
     535             :  */
     536             : static enum GNUNET_GenericReturnValue
     537           9 : parse_token_details (void *cls,
     538             :                      json_t *root,
     539             :                      struct GNUNET_JSON_Specification *ospec)
     540             : {
     541           9 :   struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
     542             :   const char *class;
     543             :   const char *ename;
     544             :   unsigned int eline;
     545             :   struct GNUNET_JSON_Specification ispec[] = {
     546           9 :     GNUNET_JSON_spec_string ("class",
     547             :                              &class),
     548           9 :     GNUNET_JSON_spec_end ()
     549             :   };
     550             : 
     551             :   (void) cls;
     552           9 :   if (GNUNET_OK !=
     553           9 :       GNUNET_JSON_parse (root,
     554             :                          ispec,
     555             :                          &ename,
     556             :                          &eline))
     557             :   {
     558           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     559             :                 "Failed to parse %s at %u: %s\n",
     560             :                 ispec[eline].field,
     561             :                 eline,
     562             :                 ename);
     563           0 :     GNUNET_break_op (0);
     564           0 :     return GNUNET_SYSERR;
     565             :   }
     566             : 
     567           9 :   family->kind = contract_token_kind_from_string (class);
     568             : 
     569           9 :   switch (family->kind)
     570             :   {
     571           0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     572           0 :     break;
     573           7 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     574             :     {
     575             :       const json_t *trusted_domains;
     576             :       struct GNUNET_JSON_Specification spec[] = {
     577           7 :         GNUNET_JSON_spec_array_const ("trusted_domains",
     578             :                                       &trusted_domains),
     579           7 :         GNUNET_JSON_spec_end ()
     580             :       };
     581             : 
     582           7 :       if (GNUNET_OK !=
     583           7 :           GNUNET_JSON_parse (root,
     584             :                              spec,
     585             :                              &ename,
     586             :                              &eline))
     587             :       {
     588           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     589             :                     "Failed to parse %s at %u: %s\n",
     590             :                     spec[eline].field,
     591             :                     eline,
     592             :                     ename);
     593           0 :         GNUNET_break_op (0);
     594           0 :         return GNUNET_SYSERR;
     595             :       }
     596             : 
     597           7 :       GNUNET_array_grow (family->details.subscription.trusted_domains,
     598             :                          family->details.subscription.trusted_domains_len,
     599             :                          json_array_size (trusted_domains));
     600             : 
     601           7 :       for (unsigned int i = 0;
     602          10 :            i < family->details.subscription.trusted_domains_len;
     603           3 :            i++)
     604             :       {
     605             :         const json_t *jdomain;
     606           3 :         jdomain = json_array_get (trusted_domains, i);
     607             : 
     608           3 :         if (! json_is_string (jdomain))
     609             :         {
     610           0 :           GNUNET_break_op (0);
     611           0 :           return GNUNET_SYSERR;
     612             :         }
     613             : 
     614           3 :         family->details.subscription.trusted_domains[i] =
     615           3 :           GNUNET_strdup (json_string_value (jdomain));
     616             :       }
     617             : 
     618           7 :       return GNUNET_OK;
     619             :     }
     620           2 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     621             :     {
     622             :       const json_t *expected_domains;
     623             :       struct GNUNET_JSON_Specification spec[] = {
     624           2 :         GNUNET_JSON_spec_array_const ("expected_domains",
     625             :                                       &expected_domains),
     626           2 :         GNUNET_JSON_spec_end ()
     627             :       };
     628             : 
     629           2 :       if (GNUNET_OK !=
     630           2 :           GNUNET_JSON_parse (root,
     631             :                              spec,
     632             :                              &ename,
     633             :                              &eline))
     634             :       {
     635           0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     636             :                     "Failed to parse %s at %u: %s\n",
     637             :                     spec[eline].field,
     638             :                     eline,
     639             :                     ename);
     640           0 :         GNUNET_break_op (0);
     641           0 :         return GNUNET_SYSERR;
     642             :       }
     643             : 
     644           2 :       GNUNET_array_grow (family->details.discount.expected_domains,
     645             :                          family->details.discount.expected_domains_len,
     646             :                          json_array_size (expected_domains));
     647             : 
     648           2 :       for (unsigned int i = 0;
     649           5 :            i < family->details.discount.expected_domains_len;
     650           3 :            i++)
     651             :       {
     652             :         const json_t *jdomain;
     653             : 
     654           3 :         jdomain = json_array_get (expected_domains,
     655             :                                   i);
     656           3 :         if (! json_is_string (jdomain))
     657             :         {
     658           0 :           GNUNET_break_op (0);
     659           0 :           return GNUNET_SYSERR;
     660             :         }
     661             : 
     662           3 :         family->details.discount.expected_domains[i] =
     663           3 :           GNUNET_strdup (json_string_value (jdomain));
     664             :       }
     665             : 
     666           2 :       return GNUNET_OK;
     667             :     }
     668             :   }
     669             : 
     670           0 :   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     671             :               "Field 'type' invalid in token family\n");
     672           0 :   GNUNET_break_op (0);
     673           0 :   return GNUNET_SYSERR;
     674             : }
     675             : 
     676             : 
     677             : /**
     678             :  * Provide specification to parse given JSON object to contract token details
     679             :  * in the contract token family. All fields from @a family are copied.
     680             :  *
     681             :  * @param name name of the token details field in the JSON
     682             :  * @param[out] token family where the token details have to be written
     683             :  */
     684             : static struct GNUNET_JSON_Specification
     685           9 : spec_token_details (const char *name,
     686             :                     struct TALER_MERCHANT_ContractTokenFamily *family)
     687             : {
     688           9 :   struct GNUNET_JSON_Specification ret = {
     689             :     .parser = &parse_token_details,
     690             :     .field = name,
     691             :     .ptr = family,
     692             :   };
     693             : 
     694           9 :   return ret;
     695             : }
     696             : 
     697             : 
     698             : /**
     699             :  * Parse given JSON object to token families array.
     700             :  *
     701             :  * @param cls closure, pointer to array length
     702             :  * @param root the json object representing the token families. The keys are
     703             :  *             the token family slugs.
     704             :  * @param[out] ospec where to write the data
     705             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     706             :  */
     707             : static enum GNUNET_GenericReturnValue
     708           8 : parse_token_families (void *cls,
     709             :                       json_t *root,
     710             :                       struct GNUNET_JSON_Specification *ospec)
     711             : {
     712           8 :   struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
     713           8 :   unsigned int *families_len = cls;
     714             :   json_t *jfamily;
     715             :   const char *slug;
     716             : 
     717           8 :   if (! json_is_object (root))
     718             :   {
     719           0 :     GNUNET_break_op (0);
     720           0 :     return GNUNET_SYSERR;
     721             :   }
     722             : 
     723          17 :   json_object_foreach (root, slug, jfamily)
     724             :   {
     725             :     const json_t *keys;
     726          18 :     struct TALER_MERCHANT_ContractTokenFamily family = {
     727           9 :       .slug = GNUNET_strdup (slug)
     728             :     };
     729             :     struct GNUNET_JSON_Specification spec[] = {
     730           9 :       GNUNET_JSON_spec_string_copy ("name",
     731             :                                     &family.name),
     732           9 :       GNUNET_JSON_spec_string_copy ("description",
     733             :                                     &family.description),
     734           9 :       GNUNET_JSON_spec_object_copy ("description_i18n",
     735             :                                     &family.description_i18n),
     736           9 :       GNUNET_JSON_spec_array_const ("keys",
     737             :                                     &keys),
     738           9 :       spec_token_details ("details",
     739             :                           &family),
     740           9 :       GNUNET_JSON_spec_bool ("critical",
     741             :                              &family.critical),
     742           9 :       GNUNET_JSON_spec_end ()
     743             :     };
     744             :     const char *error_name;
     745             :     unsigned int error_line;
     746             : 
     747           9 :     if (GNUNET_OK !=
     748           9 :         GNUNET_JSON_parse (jfamily,
     749             :                            spec,
     750             :                            &error_name,
     751             :                            &error_line))
     752             :     {
     753           0 :       GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     754             :                   "Failed to parse %s at %u: %s\n",
     755             :                   spec[error_line].field,
     756             :                   error_line,
     757             :                   error_name);
     758           0 :       GNUNET_break_op (0);
     759           0 :       return GNUNET_SYSERR;
     760             :     }
     761             : 
     762           9 :     GNUNET_array_grow (family.keys,
     763             :                        family.keys_len,
     764             :                        json_array_size (keys));
     765             : 
     766          17 :     for (unsigned int i = 0; i<family.keys_len; i++)
     767             :     {
     768           8 :       struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
     769             :       struct GNUNET_JSON_Specification key_spec[] = {
     770           8 :         TALER_JSON_spec_token_pub (
     771             :           NULL,
     772             :           &key->pub),
     773           8 :         GNUNET_JSON_spec_timestamp (
     774             :           "signature_validity_start",
     775             :           &key->valid_after),
     776           8 :         GNUNET_JSON_spec_timestamp (
     777             :           "signature_validity_end",
     778             :           &key->valid_before),
     779           8 :         GNUNET_JSON_spec_end ()
     780             :       };
     781             :       const char *ierror_name;
     782             :       unsigned int ierror_line;
     783             : 
     784           8 :       if (GNUNET_OK !=
     785           8 :           GNUNET_JSON_parse (json_array_get (keys,
     786             :                                              i),
     787             :                              key_spec,
     788             :                              &ierror_name,
     789             :                              &ierror_line))
     790             :       {
     791           0 :         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     792             :                     "Failed to parse %s at %u: %s\n",
     793             :                     key_spec[ierror_line].field,
     794             :                     ierror_line,
     795             :                     ierror_name);
     796           0 :         GNUNET_break_op (0);
     797           0 :         return GNUNET_SYSERR;
     798             :       }
     799             :     }
     800             : 
     801           9 :     GNUNET_array_append (*families,
     802             :                          *families_len,
     803             :                          family);
     804             :   }
     805             : 
     806           8 :   return GNUNET_OK;
     807             : }
     808             : 
     809             : 
     810             : /**
     811             :  * Provide specification to parse given JSON array to token families in the
     812             :  * contract terms. All fields from @a families items are copied.
     813             :  *
     814             :  * @param name name of the token families field in the JSON
     815             :  * @param[out] families where the token families array has to be written
     816             :  * @param[out] families_len length of the @a families array
     817             :  */
     818             : static struct GNUNET_JSON_Specification
     819           8 : spec_token_families (
     820             :   const char *name,
     821             :   struct TALER_MERCHANT_ContractTokenFamily **families,
     822             :   unsigned int *families_len)
     823             : {
     824           8 :   struct GNUNET_JSON_Specification ret = {
     825             :     .cls = (void *) families_len,
     826             :     .parser = &parse_token_families,
     827             :     .field = name,
     828             :     .ptr = families,
     829             :   };
     830             : 
     831           8 :   return ret;
     832             : }
     833             : 
     834             : 
     835             : enum GNUNET_GenericReturnValue
     836           2 : TALER_MERCHANT_find_token_family_key (
     837             :   const char *slug,
     838             :   struct GNUNET_TIME_Timestamp valid_after,
     839             :   const struct TALER_MERCHANT_ContractTokenFamily *families,
     840             :   unsigned int families_len,
     841             :   struct TALER_MERCHANT_ContractTokenFamily *family,
     842             :   struct TALER_MERCHANT_ContractTokenFamilyKey *key)
     843             : {
     844           3 :   for (unsigned int i = 0; i < families_len; i++)
     845             :   {
     846           3 :     const struct TALER_MERCHANT_ContractTokenFamily *fami
     847           3 :       = &families[i];
     848             : 
     849           3 :     if (0 != strcmp (fami->slug,
     850             :                      slug))
     851           1 :       continue;
     852           2 :     if (NULL != family)
     853           2 :       *family = *fami;
     854           2 :     for (unsigned int k = 0; k < fami->keys_len; k++)
     855             :     {
     856           2 :       struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
     857             : 
     858           2 :       if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
     859             :                                      ==,
     860             :                                      valid_after))
     861             :       {
     862           2 :         if (NULL != key)
     863           2 :           *key = *ki;
     864           2 :         return GNUNET_OK;
     865             :       }
     866             :     }
     867             :     /* matching family found, but no key. */
     868           0 :     return GNUNET_NO;
     869             :   }
     870             : 
     871             :   /* no matching family found */
     872           0 :   return GNUNET_SYSERR;
     873             : }
     874             : 
     875             : 
     876             : /**
     877             :  * Free all the fields in the given @a family, but not @a family itself, since
     878             :  * it is normally part of an array.
     879             :  *
     880             :  * @param[in] family contract token family to free
     881             :  */
     882             : static void
     883           9 : contract_token_family_free (
     884             :   struct TALER_MERCHANT_ContractTokenFamily *family)
     885             : {
     886           9 :   GNUNET_free (family->slug);
     887           9 :   GNUNET_free (family->name);
     888           9 :   GNUNET_free (family->description);
     889           9 :   if (NULL != family->description_i18n)
     890             :   {
     891           9 :     json_decref (family->description_i18n);
     892           9 :     family->description_i18n = NULL;
     893             :   }
     894          17 :   for (unsigned int i = 0; i < family->keys_len; i++)
     895           8 :     TALER_token_issue_pub_free (&family->keys[i].pub);
     896           9 :   GNUNET_free (family->keys);
     897             : 
     898           9 :   switch (family->kind)
     899             :   {
     900           0 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
     901           0 :     break;
     902           2 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
     903           5 :     for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
     904           3 :          i++)
     905           3 :       GNUNET_free (family->details.discount.expected_domains[i]);
     906           2 :     break;
     907           7 :   case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
     908          10 :     for (unsigned int i = 0; i < family->details.subscription.
     909           3 :          trusted_domains_len; i++)
     910           3 :       GNUNET_free (family->details.subscription.trusted_domains[i]);
     911           7 :     break;
     912             :   }
     913           9 : }
     914             : 
     915             : 
     916             : /**
     917             :  * Parse contract version of given JSON contract terms.
     918             :  *
     919             :  * @param cls closure, unused parameter
     920             :  * @param root the JSON object representing data
     921             :  * @param[out] spec where to write the data
     922             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     923             :  */
     924             : static enum GNUNET_GenericReturnValue
     925         139 : parse_contract_version (void *cls,
     926             :                         json_t *root,
     927             :                         struct GNUNET_JSON_Specification *spec)
     928             : {
     929         139 :   enum TALER_MERCHANT_ContractVersion *res
     930             :     = (enum TALER_MERCHANT_ContractVersion *) spec->ptr;
     931             : 
     932             :   (void) cls;
     933         139 :   if (json_is_integer (root))
     934             :   {
     935         139 :     json_int_t version = json_integer_value (root);
     936             : 
     937         139 :     switch (version)
     938             :     {
     939         131 :     case 0:
     940         131 :       *res = TALER_MERCHANT_CONTRACT_VERSION_0;
     941         131 :       return GNUNET_OK;
     942           8 :     case 1:
     943           8 :       *res = TALER_MERCHANT_CONTRACT_VERSION_1;
     944           8 :       return GNUNET_OK;
     945             :     }
     946             : 
     947           0 :     GNUNET_break_op (0);
     948           0 :     return GNUNET_SYSERR;
     949             :   }
     950             : 
     951           0 :   if (json_is_null (root))
     952             :   {
     953           0 :     *res = TALER_MERCHANT_CONTRACT_VERSION_0;
     954           0 :     return GNUNET_OK;
     955             :   }
     956             : 
     957           0 :   GNUNET_break_op (0);
     958           0 :   return GNUNET_SYSERR;
     959             : }
     960             : 
     961             : 
     962             : /**
     963             :  * Create JSON specification to parse a merchant contract
     964             :  * version.
     965             :  *
     966             :  * @param name name of the field
     967             :  * @param[out] version where to write the contract version
     968             :  * @return JSON specification object
     969             :  */
     970             : static struct GNUNET_JSON_Specification
     971         139 : spec_contract_version (
     972             :   const char *name,
     973             :   enum TALER_MERCHANT_ContractVersion *version)
     974             : {
     975         139 :   struct GNUNET_JSON_Specification ret = {
     976             :     .parser = &parse_contract_version,
     977             :     .field = name,
     978             :     .ptr = version
     979             :   };
     980             : 
     981         139 :   *version = TALER_MERCHANT_CONTRACT_VERSION_0;
     982         139 :   return ret;
     983             : }
     984             : 
     985             : 
     986             : /**
     987             :  * Parse v0-specific fields of @a input JSON into @a contract.
     988             :  *
     989             :  * @param[in] input the JSON contract terms
     990             :  * @param[out] contract where to write the data
     991             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     992             :  */
     993             : static enum GNUNET_GenericReturnValue
     994         131 : parse_contract_v0 (
     995             :   json_t *input,
     996             :   struct TALER_MERCHANT_Contract *contract)
     997             : {
     998             :   struct GNUNET_JSON_Specification espec[] = {
     999         131 :     TALER_JSON_spec_amount_any ("amount",
    1000             :                                 &contract->details.v0.brutto),
    1001         131 :     TALER_JSON_spec_amount_any ("max_fee",
    1002             :                                 &contract->details.v0.max_fee),
    1003         131 :     GNUNET_JSON_spec_end ()
    1004             :   };
    1005             :   enum GNUNET_GenericReturnValue res;
    1006             :   const char *ename;
    1007             :   unsigned int eline;
    1008             : 
    1009         131 :   res = GNUNET_JSON_parse (input,
    1010             :                            espec,
    1011             :                            &ename,
    1012             :                            &eline);
    1013         131 :   if (GNUNET_OK != res)
    1014             :   {
    1015           0 :     GNUNET_break (0);
    1016           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1017             :                 "Failed to parse contract v0 at field %s\n",
    1018             :                 ename);
    1019           0 :     return GNUNET_SYSERR;
    1020             :   }
    1021             : 
    1022         131 :   if (GNUNET_OK !=
    1023         131 :       TALER_amount_cmp_currency (&contract->details.v0.max_fee,
    1024         131 :                                  &contract->details.v0.brutto))
    1025             :   {
    1026           0 :     GNUNET_break (0);
    1027           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1028             :                 "'max_fee' in database does not match currency of contract price");
    1029           0 :     return GNUNET_SYSERR;
    1030             :   }
    1031             : 
    1032         131 :   return res;
    1033             : }
    1034             : 
    1035             : 
    1036             : /**
    1037             :  * Parse v1-specific fields of @a input JSON into @a contract.
    1038             :  *
    1039             :  * @param[in] input the JSON contract terms
    1040             :  * @param[out] contract where to write the data
    1041             :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    1042             :  */
    1043             : static enum GNUNET_GenericReturnValue
    1044           8 : parse_contract_v1 (
    1045             :   json_t *input,
    1046             :   struct TALER_MERCHANT_Contract *contract)
    1047             : {
    1048             :   struct GNUNET_JSON_Specification espec[] = {
    1049           8 :     spec_choices ("choices",
    1050             :                   &contract->details.v1.choices,
    1051             :                   &contract->details.v1.choices_len),
    1052           8 :     spec_token_families ("token_families",
    1053             :                          &contract->details.v1.token_authorities,
    1054             :                          &contract->details.v1.
    1055             :                          token_authorities_len),
    1056           8 :     GNUNET_JSON_spec_end ()
    1057             :   };
    1058             : 
    1059             :   enum GNUNET_GenericReturnValue res;
    1060             :   const char *ename;
    1061             :   unsigned int eline;
    1062             : 
    1063           8 :   res = GNUNET_JSON_parse (input,
    1064             :                            espec,
    1065             :                            &ename,
    1066             :                            &eline);
    1067             : 
    1068           8 :   if (GNUNET_OK != res)
    1069             :   {
    1070           0 :     GNUNET_break (0);
    1071           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1072             :                 "Failed to parse contract v1 at field %s\n",
    1073             :                 ename);
    1074           0 :     return GNUNET_SYSERR;
    1075             :   }
    1076             : 
    1077           8 :   return res;
    1078             : }
    1079             : 
    1080             : 
    1081             : struct TALER_MERCHANT_Contract *
    1082         139 : TALER_MERCHANT_contract_parse (json_t *input,
    1083             :                                bool nonce_optional)
    1084             : {
    1085             :   struct TALER_MERCHANT_Contract *contract
    1086         139 :     = GNUNET_new (struct TALER_MERCHANT_Contract);
    1087             :   struct GNUNET_JSON_Specification espec[] = {
    1088         139 :     spec_contract_version ("version",
    1089             :                            &contract->version),
    1090         139 :     GNUNET_JSON_spec_string_copy ("summary",
    1091             :                                   &contract->summary),
    1092             :     /* FIXME: do i18n_str validation in the future */
    1093         139 :     GNUNET_JSON_spec_mark_optional (
    1094             :       GNUNET_JSON_spec_object_copy ("summary_i18n",
    1095             :                                     &contract->summary_i18n),
    1096             :       NULL),
    1097         139 :     GNUNET_JSON_spec_string_copy ("order_id",
    1098             :                                   &contract->order_id),
    1099         139 :     GNUNET_JSON_spec_mark_optional (
    1100             :       GNUNET_JSON_spec_string_copy ("public_reorder_url",
    1101             :                                     &contract->public_reorder_url),
    1102             :       NULL),
    1103         139 :     GNUNET_JSON_spec_mark_optional (
    1104             :       GNUNET_JSON_spec_string_copy ("fulfillment_url",
    1105             :                                     &contract->fulfillment_url),
    1106             :       NULL),
    1107         139 :     GNUNET_JSON_spec_mark_optional (
    1108             :       GNUNET_JSON_spec_string_copy ("fulfillment_message",
    1109             :                                     &contract->fulfillment_message),
    1110             :       NULL),
    1111         139 :     GNUNET_JSON_spec_mark_optional (
    1112             :       GNUNET_JSON_spec_object_copy ("fulfillment_message_i18n",
    1113             :                                     &contract->fulfillment_message_i18n),
    1114             :       NULL),
    1115         139 :     GNUNET_JSON_spec_array_copy ("products",
    1116             :                                  &contract->products),
    1117         139 :     GNUNET_JSON_spec_timestamp ("timestamp",
    1118             :                                 &contract->timestamp),
    1119         139 :     GNUNET_JSON_spec_timestamp ("refund_deadline",
    1120             :                                 &contract->refund_deadline),
    1121         139 :     GNUNET_JSON_spec_timestamp ("pay_deadline",
    1122             :                                 &contract->pay_deadline),
    1123         139 :     GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
    1124             :                                 &contract->wire_deadline),
    1125         139 :     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
    1126             :                                  &contract->merchant_pub),
    1127         139 :     GNUNET_JSON_spec_string_copy ("merchant_base_url",
    1128             :                                   &contract->merchant_base_url),
    1129         139 :     spec_merchant_details ("merchant",
    1130             :                            contract),
    1131         139 :     GNUNET_JSON_spec_fixed_auto ("h_wire",
    1132             :                                  &contract->h_wire),
    1133         139 :     GNUNET_JSON_spec_string_copy ("wire_method",
    1134             :                                   &contract->wire_method),
    1135         139 :     GNUNET_JSON_spec_array_copy ("exchanges",
    1136             :                                  &contract->exchanges),
    1137         139 :     GNUNET_JSON_spec_mark_optional (
    1138             :       GNUNET_JSON_spec_object_copy ("delivery_location",
    1139             :                                     &contract->delivery_location),
    1140             :       NULL),
    1141         139 :     GNUNET_JSON_spec_mark_optional (
    1142             :       GNUNET_JSON_spec_timestamp ("delivery_date",
    1143             :                                   &contract->delivery_date),
    1144             :       NULL),
    1145             :     (nonce_optional)
    1146         137 :     ? GNUNET_JSON_spec_mark_optional (
    1147             :       GNUNET_JSON_spec_string_copy ("nonce",
    1148             :                                     &contract->nonce),
    1149             :       NULL)
    1150         139 :     : GNUNET_JSON_spec_string_copy ("nonce",
    1151             :                                     &contract->nonce),
    1152         139 :     GNUNET_JSON_spec_mark_optional (
    1153             :       GNUNET_JSON_spec_relative_time ("auto_refund",
    1154             :                                       &contract->auto_refund),
    1155             :       NULL),
    1156         139 :     GNUNET_JSON_spec_mark_optional (
    1157             :       GNUNET_JSON_spec_object_copy ("extra",
    1158             :                                     &contract->extra),
    1159             :       NULL),
    1160         139 :     GNUNET_JSON_spec_mark_optional (
    1161             :       GNUNET_JSON_spec_uint8 ("minimum_age",
    1162             :                               &contract->minimum_age),
    1163             :       NULL),
    1164         139 :     GNUNET_JSON_spec_end ()
    1165             :   };
    1166             : 
    1167             :   enum GNUNET_GenericReturnValue res;
    1168             :   const char *ename;
    1169             :   unsigned int eline;
    1170             : 
    1171         139 :   GNUNET_assert (NULL != input);
    1172         139 :   res = GNUNET_JSON_parse (input,
    1173             :                            espec,
    1174             :                            &ename,
    1175             :                            &eline);
    1176         139 :   if (GNUNET_OK != res)
    1177             :   {
    1178           0 :     GNUNET_break (0);
    1179           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1180             :                 "Failed to parse contract at field %s\n",
    1181             :                 ename);
    1182           0 :     goto cleanup;
    1183             :   }
    1184             : 
    1185         139 :   switch (contract->version)
    1186             :   {
    1187         131 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1188         131 :     res = parse_contract_v0 (input,
    1189             :                              contract);
    1190         131 :     break;
    1191           8 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1192           8 :     res = parse_contract_v1 (input,
    1193             :                              contract);
    1194           8 :     break;
    1195             :   }
    1196             : 
    1197         139 :   if (GNUNET_OK != res)
    1198             :   {
    1199           0 :     GNUNET_break (0);
    1200           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    1201             :                 "Failed to parse contract\n");
    1202           0 :     goto cleanup;
    1203             :   }
    1204         139 :   return contract;
    1205             : 
    1206           0 : cleanup:
    1207           0 :   TALER_MERCHANT_contract_free (contract);
    1208           0 :   return NULL;
    1209             : }
    1210             : 
    1211             : 
    1212             : void
    1213         139 : TALER_MERCHANT_contract_free (
    1214             :   struct TALER_MERCHANT_Contract *contract)
    1215             : {
    1216         139 :   if (NULL == contract)
    1217           0 :     return;
    1218         139 :   GNUNET_free (contract->public_reorder_url);
    1219         139 :   GNUNET_free (contract->order_id);
    1220         139 :   GNUNET_free (contract->merchant_base_url);
    1221         139 :   GNUNET_free (contract->merchant.name);
    1222         139 :   GNUNET_free (contract->merchant.website);
    1223         139 :   GNUNET_free (contract->merchant.email);
    1224         139 :   GNUNET_free (contract->merchant.logo);
    1225         139 :   if (NULL != contract->merchant.address)
    1226             :   {
    1227         139 :     json_decref (contract->merchant.address);
    1228         139 :     contract->merchant.address = NULL;
    1229             :   }
    1230         139 :   if (NULL != contract->merchant.jurisdiction)
    1231             :   {
    1232         139 :     json_decref (contract->merchant.jurisdiction);
    1233         139 :     contract->merchant.jurisdiction = NULL;
    1234             :   }
    1235         139 :   GNUNET_free (contract->summary);
    1236         139 :   GNUNET_free (contract->fulfillment_url);
    1237         139 :   GNUNET_free (contract->fulfillment_message);
    1238         139 :   if (NULL != contract->fulfillment_message_i18n)
    1239             :   {
    1240           2 :     json_decref (contract->fulfillment_message_i18n);
    1241           2 :     contract->fulfillment_message_i18n = NULL;
    1242             :   }
    1243         139 :   if (NULL != contract->products)
    1244             :   {
    1245         139 :     json_decref (contract->products);
    1246         139 :     contract->products = NULL;
    1247             :   }
    1248         139 :   GNUNET_free (contract->wire_method);
    1249         139 :   if (NULL != contract->exchanges)
    1250             :   {
    1251         139 :     json_decref (contract->exchanges);
    1252         139 :     contract->exchanges = NULL;
    1253             :   }
    1254         139 :   if (NULL != contract->delivery_location)
    1255             :   {
    1256           2 :     json_decref (contract->delivery_location);
    1257           2 :     contract->delivery_location = NULL;
    1258             :   }
    1259         139 :   GNUNET_free (contract->nonce);
    1260         139 :   if (NULL != contract->extra)
    1261             :   {
    1262           2 :     json_decref (contract->extra);
    1263           2 :     contract->extra = NULL;
    1264             :   }
    1265             : 
    1266         139 :   switch (contract->version)
    1267             :   {
    1268         131 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
    1269         131 :     break;
    1270           8 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
    1271           8 :     for (unsigned int i = 0;
    1272          17 :          i < contract->details.v1.choices_len;
    1273           9 :          i++)
    1274           9 :       contract_choice_free (&contract->details.v1.choices[i]);
    1275           8 :     GNUNET_free (contract->details.v1.choices);
    1276           8 :     for (unsigned int i = 0;
    1277          17 :          i < contract->details.v1.token_authorities_len;
    1278           9 :          i++)
    1279           9 :       contract_token_family_free (&contract->details.v1.token_authorities[i]);
    1280           8 :     GNUNET_free (contract->details.v1.token_authorities);
    1281           8 :     break;
    1282             :   }
    1283         139 :   GNUNET_free (contract);
    1284             : }

Generated by: LCOV version 1.16