merchant

Merchant backend to process payments, run by merchants
Log | Files | Refs | Submodules | README | LICENSE

contract_parse.c (38885B)


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