merchant

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

order_choice_parse.c (14326B)


      1 /*
      2   This file is part of TALER
      3   (C) 2024, 2025 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU Lesser General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15 */
     16 /**
     17  * @file src/util/order_choice_parse.c
     18  * @brief shared logic for order choice parsing
     19  * @author Iván Ávalos
     20  * @author Christian Grothoff
     21  */
     22 #include "platform.h"
     23 #include <gnunet/gnunet_common.h>
     24 #include <gnunet/gnunet_json_lib.h>
     25 #include <jansson.h>
     26 #include <stdbool.h>
     27 #include <stdint.h>
     28 #include <taler/taler_json_lib.h>
     29 #include <taler/taler_util.h>
     30 #include "taler/taler_merchant_util.h"
     31 
     32 
     33 /**
     34  * Maximum number of entries we accept in the @e choices array of a v1
     35  * order.  The array is grown to its JSON length before any entry is
     36  * parsed, so the length must be bounded independently of the maximum
     37  * upload size.
     38  */
     39 #define MAX_CHOICES 1024
     40 
     41 
     42 /**
     43  * Parse JSON order choice input.
     44  *
     45  * @param[in] root JSON object containing choice input
     46  * @param[out] input parsed choice input, NULL if @a input is malformed
     47  * @param index index of choice input in inputs array
     48  * @return #GNUNET_SYSERR if @a input is malformed; #GNUNET_OK otherwise
     49  */
     50 static enum GNUNET_GenericReturnValue
     51 parse_order_choice_input (
     52   json_t *root,
     53   struct TALER_MERCHANT_OrderInput *input,
     54   size_t index)
     55 {
     56   const char *ename;
     57   unsigned int eline;
     58   struct GNUNET_JSON_Specification ispec[] = {
     59     TALER_MERCHANT_json_spec_cit ("type",
     60                                   &input->type),
     61     GNUNET_JSON_spec_end ()
     62   };
     63 
     64   if (GNUNET_OK !=
     65       GNUNET_JSON_parse (root,
     66                          ispec,
     67                          &ename,
     68                          &eline))
     69   {
     70     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     71                 "Failed to parse %s at %u: %s\n",
     72                 ispec[eline].field,
     73                 eline,
     74                 ename);
     75     GNUNET_break_op (0);
     76     return GNUNET_SYSERR;
     77   }
     78 
     79   switch (input->type)
     80   {
     81   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
     82     GNUNET_break (0);
     83     break;
     84   case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
     85     {
     86       struct GNUNET_JSON_Specification spec[] = {
     87         TALER_JSON_spec_slug ("token_family_slug",
     88                               &input->details.token.token_family_slug),
     89         GNUNET_JSON_spec_mark_optional (
     90           GNUNET_JSON_spec_uint ("count",
     91                                  &input->details.token.count),
     92           NULL),
     93         GNUNET_JSON_spec_end ()
     94       };
     95 
     96       if (GNUNET_OK !=
     97           GNUNET_JSON_parse (root,
     98                              spec,
     99                              &ename,
    100                              &eline))
    101       {
    102         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    103                     "Failed to parse %s at %u: %s\n",
    104                     spec[eline].field,
    105                     eline,
    106                     ename);
    107         GNUNET_break_op (0);
    108         return GNUNET_SYSERR;
    109       }
    110 
    111       return GNUNET_OK;
    112     }
    113   }
    114 
    115   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    116               "Field 'type' invalid in input #%u\n",
    117               (unsigned int) index);
    118   GNUNET_break_op (0);
    119   return GNUNET_SYSERR;
    120 }
    121 
    122 
    123 /**
    124  * Parse JSON order choice output.
    125  *
    126  * @param[in] root JSON object containing choice output
    127  * @param[out] output parsed choice output, NULL if @a output is malformed
    128  * @param index index of choice output in outputs array
    129  * @return #GNUNET_SYSERR if @a output is malformed; #GNUNET_OK otherwise
    130  */
    131 static enum GNUNET_GenericReturnValue
    132 parse_order_choice_output (
    133   json_t *root,
    134   struct TALER_MERCHANT_OrderOutput *output,
    135   size_t index)
    136 {
    137   const char *ename;
    138   unsigned int eline;
    139   struct GNUNET_JSON_Specification ispec[] = {
    140     TALER_MERCHANT_json_spec_cot ("type",
    141                                   &output->type),
    142     GNUNET_JSON_spec_end ()
    143   };
    144 
    145   if (GNUNET_OK !=
    146       GNUNET_JSON_parse (root,
    147                          ispec,
    148                          &ename,
    149                          &eline))
    150   {
    151     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    152                 "Failed to parse %s at %u: %s\n",
    153                 ispec[eline].field,
    154                 eline,
    155                 ename);
    156     GNUNET_break_op (0);
    157     return GNUNET_SYSERR;
    158   }
    159 
    160   switch (output->type)
    161   {
    162   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
    163     GNUNET_break (0);
    164     break;
    165   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
    166     {
    167       struct GNUNET_JSON_Specification spec[] = {
    168         TALER_JSON_spec_slug ("token_family_slug",
    169                               &output->details.token.token_family_slug),
    170         GNUNET_JSON_spec_mark_optional (
    171           GNUNET_JSON_spec_uint ("count",
    172                                  &output->details.token.count),
    173           NULL),
    174         GNUNET_JSON_spec_mark_optional (
    175           GNUNET_JSON_spec_timestamp ("valid_at",
    176                                       &output->details.token.valid_at),
    177           NULL),
    178         GNUNET_JSON_spec_end ()
    179       };
    180 
    181       if (GNUNET_OK !=
    182           GNUNET_JSON_parse (root,
    183                              spec,
    184                              &ename,
    185                              &eline))
    186       {
    187         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    188                     "Failed to parse %s at %u: %s\n",
    189                     spec[eline].field,
    190                     eline,
    191                     ename);
    192         GNUNET_break_op (0);
    193         return GNUNET_SYSERR;
    194       }
    195 
    196       return GNUNET_OK;
    197     }
    198   case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
    199     {
    200       struct GNUNET_JSON_Specification spec[] = {
    201         GNUNET_JSON_spec_mark_optional (
    202           TALER_JSON_spec_amount_any ("amount",
    203                                       &output->details.donation_receipt.amount),
    204           &output->details.donation_receipt.no_amount),
    205         GNUNET_JSON_spec_end ()
    206       };
    207 
    208       if (GNUNET_OK !=
    209           GNUNET_JSON_parse (root,
    210                              spec,
    211                              &ename,
    212                              &eline))
    213       {
    214         GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    215                     "Failed to parse %s at %u: %s\n",
    216                     spec[eline].field,
    217                     eline,
    218                     ename);
    219         GNUNET_break_op (0);
    220         return GNUNET_SYSERR;
    221       }
    222       return GNUNET_OK;
    223     }
    224   }
    225 
    226   GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
    227               "Field 'type' invalid in output #%u\n",
    228               (unsigned int) index);
    229   GNUNET_break_op (0);
    230   return GNUNET_SYSERR;
    231 }
    232 
    233 
    234 /**
    235  * Parse given JSON object to choices array.
    236  *
    237  * @param cls closure, pointer to array length
    238  * @param root the json array representing the choices
    239  * @param[out] ospec where to write the data
    240  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
    241  */
    242 static enum GNUNET_GenericReturnValue
    243 parse_order_choices (
    244   void *cls,
    245   json_t *root,
    246   struct GNUNET_JSON_Specification *ospec)
    247 {
    248   struct TALER_MERCHANT_OrderChoice **choices = ospec->ptr;
    249   unsigned int *choices_len = cls;
    250 
    251   if (! json_is_array (root))
    252   {
    253     GNUNET_break_op (0);
    254     return GNUNET_SYSERR;
    255   }
    256   if (0 == json_array_size (root))
    257   {
    258     /* empty list of choices is not allowed */
    259     GNUNET_break_op (0);
    260     return GNUNET_SYSERR;
    261   }
    262   if (MAX_CHOICES < json_array_size (root))
    263   {
    264     /* 'choices' is untrusted input; reject rather than abort in
    265        GNUNET_xmalloc_() (which asserts above 40 MiB). */
    266     GNUNET_break_op (0);
    267     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    268                 "Too many choices in order (%u, at most %u allowed)\n",
    269                 (unsigned int) json_array_size (root),
    270                 (unsigned int) MAX_CHOICES);
    271     return GNUNET_SYSERR;
    272   }
    273   *choices = NULL;
    274   *choices_len = 0;
    275   GNUNET_array_grow (*choices,
    276                      *choices_len,
    277                      json_array_size (root));
    278 
    279   for (unsigned int i = 0; i < *choices_len; i++)
    280   {
    281     struct TALER_MERCHANT_OrderChoice *choice = &(*choices)[i];
    282     const json_t *jinputs = NULL;
    283     const json_t *joutputs = NULL;
    284     struct GNUNET_JSON_Specification spec[] = {
    285       TALER_JSON_spec_amount_any ("amount",
    286                                   &choice->amount),
    287       GNUNET_JSON_spec_mark_optional (
    288         TALER_JSON_spec_amount_any ("tip",
    289                                     &choice->tip),
    290         &choice->no_tip),
    291       GNUNET_JSON_spec_mark_optional (
    292         GNUNET_JSON_spec_string_copy ("description",
    293                                       &choice->description),
    294         NULL),
    295       GNUNET_JSON_spec_mark_optional (
    296         GNUNET_JSON_spec_object_copy ("description_i18n",
    297                                       &choice->description_i18n),
    298         NULL),
    299       GNUNET_JSON_spec_mark_optional (
    300         TALER_JSON_spec_amount_any ("max_fee",
    301                                     &choice->max_fee),
    302         &choice->no_max_fee),
    303       GNUNET_JSON_spec_mark_optional (
    304         GNUNET_JSON_spec_bool ("editable_amount",
    305                                &choice->editable_amount),
    306         NULL),
    307       GNUNET_JSON_spec_mark_optional (
    308         GNUNET_JSON_spec_array_const ("inputs",
    309                                       &jinputs),
    310         NULL),
    311       GNUNET_JSON_spec_mark_optional (
    312         GNUNET_JSON_spec_array_const ("outputs",
    313                                       &joutputs),
    314         NULL),
    315       GNUNET_JSON_spec_end ()
    316     };
    317     const char *ename;
    318     unsigned int eline;
    319 
    320     if (GNUNET_OK !=
    321         GNUNET_JSON_parse (json_array_get (root, i),
    322                            spec,
    323                            &ename,
    324                            &eline))
    325     {
    326       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    327                   "Failed to parse %s at %u: %s\n",
    328                   spec[eline].field,
    329                   eline,
    330                   ename);
    331       GNUNET_break_op (0);
    332       return GNUNET_SYSERR;
    333     }
    334     if ( (! choice->no_max_fee) &&
    335          (GNUNET_OK !=
    336           TALER_amount_cmp_currency (&choice->amount,
    337                                      &choice->max_fee)) )
    338     {
    339       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    340                   "Fee currency does not match amount currency in choice #%u\n",
    341                   i);
    342       GNUNET_break_op (0);
    343       return GNUNET_SYSERR;
    344     }
    345     if ( (! choice->no_tip) &&
    346          (GNUNET_OK !=
    347           TALER_amount_cmp_currency (&choice->amount,
    348                                      &choice->tip)) )
    349     {
    350       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    351                   "Tip currency does not match amount currency in choice #%u\n",
    352                   i);
    353       GNUNET_break_op (0);
    354       return GNUNET_SYSERR;
    355     }
    356 
    357     if (NULL != jinputs)
    358     {
    359       const json_t *jinput;
    360       size_t idx;
    361 
    362       json_array_foreach ((json_t *) jinputs, idx, jinput)
    363       {
    364         struct TALER_MERCHANT_OrderInput input = {
    365           .details.token.count = 1
    366         };
    367 
    368         if (GNUNET_OK !=
    369             parse_order_choice_input ((json_t *) jinput,
    370                                       &input,
    371                                       idx))
    372         {
    373           GNUNET_break (0);
    374           return GNUNET_SYSERR;
    375         }
    376         switch (input.type)
    377         {
    378         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
    379           GNUNET_break_op (0);
    380           return GNUNET_SYSERR;
    381         case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
    382           /* Ignore inputs tokens with 'count' field set to 0 */
    383           if (0 == input.details.token.count)
    384             continue;
    385           break;
    386         }
    387         GNUNET_array_append (choice->inputs,
    388                              choice->inputs_len,
    389                              input);
    390       }
    391     }
    392 
    393     if (NULL != joutputs)
    394     {
    395       const json_t *joutput;
    396       size_t idx;
    397       json_array_foreach ((json_t *) joutputs, idx, joutput)
    398       {
    399         struct TALER_MERCHANT_OrderOutput output = {
    400           .details.token.count = 1
    401         };
    402 
    403         if (GNUNET_OK !=
    404             parse_order_choice_output ((json_t *) joutput,
    405                                        &output,
    406                                        idx))
    407         {
    408           GNUNET_break (0);
    409           return GNUNET_SYSERR;
    410         }
    411         switch (output.type)
    412         {
    413         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
    414           GNUNET_break_op (0);
    415           return GNUNET_SYSERR;
    416         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
    417           /* Ignore output tokens with 'count' field set to 0 */
    418           if (0 == output.details.token.count)
    419             continue;
    420           break;
    421         case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
    422           break;
    423         }
    424         GNUNET_array_append (choice->outputs,
    425                              choice->outputs_len,
    426                              output);
    427       }
    428     }
    429   }
    430 
    431   return GNUNET_OK;
    432 }
    433 
    434 
    435 struct GNUNET_JSON_Specification
    436 TALER_MERCHANT_spec_order_choices (
    437   const char *name,
    438   struct TALER_MERCHANT_OrderChoice **choices,
    439   unsigned int *choices_len)
    440 {
    441   struct GNUNET_JSON_Specification ret = {
    442     .cls = (void *) choices_len,
    443     .parser = &parse_order_choices,
    444     .field = name,
    445     .ptr = choices,
    446   };
    447 
    448   return ret;
    449 }
    450 
    451 
    452 void
    453 TALER_MERCHANT_order_choice_free (
    454   struct TALER_MERCHANT_OrderChoice *choice)
    455 {
    456   for (unsigned int i = 0; i < choice->outputs_len; i++)
    457   {
    458     struct TALER_MERCHANT_OrderOutput *output = &choice->outputs[i];
    459 
    460     switch (output->type)
    461     {
    462     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
    463       GNUNET_break (0);
    464       break;
    465     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
    466       break;
    467     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
    468       break;
    469 #if FUTURE
    470     case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
    471       GNUNET_free (output->details.coin.exchange_url);
    472       break;
    473 #endif
    474     }
    475   }
    476   GNUNET_free (choice->description);
    477   if (NULL != choice->description_i18n)
    478   {
    479     json_decref (choice->description_i18n);
    480     choice->description_i18n = NULL;
    481   }
    482   GNUNET_free (choice->inputs);
    483   GNUNET_free (choice->outputs);
    484 }