LCOV - code coverage report
Current view: top level - util - order_parse.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 68.6 % 140 96
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2024, 2025 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or modify it under the
       6              :   terms of the GNU Lesser General Public License as published by the Free Software
       7              :   Foundation; either version 3, or (at your option) any later version.
       8              : 
       9              :   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
      10              :   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
      11              :   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
      12              : 
      13              :   You should have received a copy of the GNU General Public License along with
      14              :   TALER; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
      15              : */
      16              : /**
      17              :  * @file src/util/order_parse.c
      18              :  * @brief shared logic for order 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              :  * Parse v0-specific fields of @a input JSON into @a order.
      35              :  *
      36              :  * @param[in] input the JSON order terms
      37              :  * @param[out] order where to write the data
      38              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
      39              :  */
      40              : static enum GNUNET_GenericReturnValue
      41          105 : parse_order_v0 (
      42              :   json_t *input,
      43              :   struct TALER_MERCHANT_Order *order)
      44              : {
      45              :   struct GNUNET_JSON_Specification espec[] = {
      46          105 :     TALER_JSON_spec_amount_any ("amount",
      47              :                                 &order->details.v0.brutto),
      48          105 :     GNUNET_JSON_spec_mark_optional (
      49              :       TALER_JSON_spec_amount_any ("tip",
      50              :                                   &order->details.v0.tip),
      51              :       &order->details.v0.no_tip),
      52          105 :     GNUNET_JSON_spec_mark_optional (
      53              :       TALER_JSON_spec_amount_any ("max_fee",
      54              :                                   &order->details.v0.max_fee),
      55              :       &order->details.v0.no_max_fee),
      56          105 :     GNUNET_JSON_spec_end ()
      57              :   };
      58              :   enum GNUNET_GenericReturnValue res;
      59              :   const char *ename;
      60              :   unsigned int eline;
      61              : 
      62          105 :   res = GNUNET_JSON_parse (input,
      63              :                            espec,
      64              :                            &ename,
      65              :                            &eline);
      66          105 :   if (GNUNET_OK != res)
      67              :   {
      68            0 :     GNUNET_break (0);
      69            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      70              :                 "Failed to parse order v0 at field %s\n",
      71              :                 ename);
      72            0 :     return GNUNET_SYSERR;
      73              :   }
      74              : 
      75          115 :   if ( (! order->details.v0.no_max_fee) &&
      76              :        (GNUNET_OK !=
      77           10 :         TALER_amount_cmp_currency (&order->details.v0.max_fee,
      78           10 :                                    &order->details.v0.brutto)) )
      79              :   {
      80            0 :     GNUNET_break (0);
      81            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      82              :                 "'max_fee' does not match currency of order price");
      83            0 :     return GNUNET_SYSERR;
      84              :   }
      85          105 :   if ( (! order->details.v0.no_tip) &&
      86              :        (GNUNET_OK !=
      87            0 :         TALER_amount_cmp_currency (&order->details.v0.tip,
      88            0 :                                    &order->details.v0.brutto)) )
      89              :   {
      90            0 :     GNUNET_break (0);
      91            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
      92              :                 "'tip' does not match currency of order price");
      93            0 :     return GNUNET_SYSERR;
      94              :   }
      95          105 :   if (NULL != order->base->amount_external)
      96              :   {
      97           14 :     if (! TALER_MERCHANT_amount_external_valid (
      98           14 :           order->base->amount_external))
      99              :     {
     100            4 :       GNUNET_break_op (0);
     101            4 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     102              :                   "'amount_external' is not a valid set of external payments");
     103            4 :       return GNUNET_SYSERR;
     104              :     }
     105           10 :     if (! TALER_MERCHANT_amount_external_currency_valid (
     106           10 :           order->base->amount_external,
     107           10 :           &order->details.v0.brutto))
     108              :     {
     109            2 :       GNUNET_break_op (0);
     110            2 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     111              :                   "'amount_external' does not match currency of order price");
     112            2 :       return GNUNET_SYSERR;
     113              :     }
     114            8 :     if (! TALER_MERCHANT_amount_external_total_valid (
     115            8 :           order->base->amount_external,
     116            8 :           &order->details.v0.brutto))
     117              :     {
     118            0 :       GNUNET_break_op (0);
     119            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     120              :                   "total of 'amount_external' and order price is too large");
     121            0 :       return GNUNET_SYSERR;
     122              :     }
     123              :   }
     124              : 
     125           99 :   return res;
     126              : }
     127              : 
     128              : 
     129              : /**
     130              :  * Parse v1-specific fields of @a input JSON into @a order.
     131              :  *
     132              :  * @param[in] input the JSON order terms
     133              :  * @param[out] order where to write the data
     134              :  * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
     135              :  */
     136              : static enum GNUNET_GenericReturnValue
     137           28 : parse_order_v1 (
     138              :   json_t *input,
     139              :   struct TALER_MERCHANT_Order *order)
     140              : {
     141              :   struct GNUNET_JSON_Specification espec[] = {
     142           28 :     TALER_MERCHANT_spec_order_choices (
     143              :       "choices",
     144              :       &order->details.v1.choices,
     145              :       &order->details.v1.choices_len),
     146           28 :     GNUNET_JSON_spec_end ()
     147              :   };
     148              : 
     149              :   enum GNUNET_GenericReturnValue res;
     150              :   const char *ename;
     151              :   unsigned int eline;
     152              : 
     153           28 :   res = GNUNET_JSON_parse (input,
     154              :                            espec,
     155              :                            &ename,
     156              :                            &eline);
     157           28 :   if (GNUNET_OK != res)
     158              :   {
     159            0 :     GNUNET_break (0);
     160            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     161              :                 "Failed to parse order v1 at field %s\n",
     162              :                 ename);
     163            0 :     return GNUNET_SYSERR;
     164              :   }
     165           28 :   if (NULL != order->base->amount_external)
     166              :   {
     167            0 :     if (! TALER_MERCHANT_amount_external_valid (order->base->amount_external))
     168              :     {
     169            0 :       GNUNET_break_op (0);
     170            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     171              :                   "'amount_external' is not a valid set of external payments\n");
     172            0 :       return GNUNET_SYSERR;
     173              :     }
     174            0 :     for (unsigned int i = 0; i<order->details.v1.choices_len; i++)
     175              :     {
     176            0 :       if (! TALER_MERCHANT_amount_external_currency_valid (
     177            0 :             order->base->amount_external,
     178            0 :             &order->details.v1.choices[i].amount))
     179              :       {
     180            0 :         GNUNET_break_op (0);
     181            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     182              :                     "'amount_external' does not match currency of choice #%u\n",
     183              :                     i);
     184            0 :         return GNUNET_SYSERR;
     185              :       }
     186            0 :       if (! TALER_MERCHANT_amount_external_total_valid (
     187            0 :             order->base->amount_external,
     188            0 :             &order->details.v1.choices[i].amount))
     189              :       {
     190            0 :         GNUNET_break_op (0);
     191            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     192              :                     "total of 'amount_external' and choice #%u is too large\n",
     193              :                     i);
     194            0 :         return GNUNET_SYSERR;
     195              :       }
     196              :     }
     197              :   }
     198              : 
     199           28 :   return res;
     200              : }
     201              : 
     202              : 
     203              : struct TALER_MERCHANT_Order *
     204          133 : TALER_MERCHANT_order_parse (json_t *input)
     205              : {
     206              :   struct TALER_MERCHANT_Order *order
     207          133 :     = GNUNET_new (struct TALER_MERCHANT_Order);
     208          133 :   enum GNUNET_GenericReturnValue res
     209              :     = GNUNET_SYSERR;
     210              : 
     211          133 :   GNUNET_assert (NULL != input);
     212          133 :   order->base = TALER_MERCHANT_base_terms_parse (input);
     213          133 :   if (NULL == order->base)
     214              :   {
     215            0 :     GNUNET_break (0);
     216            0 :     GNUNET_free (order);
     217            0 :     return NULL;
     218              :   }
     219          133 :   order->refund_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
     220          133 :   order->wire_transfer_deadline = GNUNET_TIME_UNIT_FOREVER_TS;
     221              :   {
     222              :     const json_t *products;
     223              :     struct GNUNET_JSON_Specification espec[] = {
     224          133 :       GNUNET_JSON_spec_mark_optional (
     225              :         GNUNET_JSON_spec_array_const ("products",
     226              :                                       &products),
     227              :         NULL),
     228          133 :       GNUNET_JSON_spec_mark_optional (
     229              :         TALER_JSON_spec_slug_copy ("order_id",
     230              :                                    &order->order_id),
     231              :         NULL),
     232          133 :       GNUNET_JSON_spec_mark_optional (
     233              :         GNUNET_JSON_spec_timestamp ("timestamp",
     234              :                                     &order->timestamp),
     235              :         NULL),
     236          133 :       GNUNET_JSON_spec_mark_optional (
     237              :         GNUNET_JSON_spec_timestamp ("refund_deadline",
     238              :                                     &order->refund_deadline),
     239              :         NULL),
     240          133 :       GNUNET_JSON_spec_mark_optional (
     241              :         GNUNET_JSON_spec_timestamp ("pay_deadline",
     242              :                                     &order->pay_deadline),
     243              :         NULL),
     244          133 :       GNUNET_JSON_spec_mark_optional (
     245              :         GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
     246              :                                     &order->wire_transfer_deadline),
     247              :         NULL),
     248          133 :       GNUNET_JSON_spec_end ()
     249              :     };
     250              :     const char *ename;
     251              :     unsigned int eline;
     252              : 
     253          133 :     res = GNUNET_JSON_parse (input,
     254              :                              espec,
     255              :                              &ename,
     256              :                              &eline);
     257          133 :     if (GNUNET_OK != res)
     258              :     {
     259            0 :       GNUNET_break_op (0);
     260            0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     261              :                   "Failed to parse proto contract at field %s\n",
     262              :                   ename);
     263            0 :       goto cleanup;
     264              :     }
     265          133 :     if (NULL != products)
     266              :     {
     267           17 :       order->products_len = json_array_size (products);
     268           17 :       if (0 != order->products_len)
     269              :       {
     270              :         size_t i;
     271              :         json_t *p;
     272              : 
     273           11 :         order->products = GNUNET_new_array (
     274              :           order->products_len,
     275              :           struct TALER_MERCHANT_ProductSold);
     276           22 :         json_array_foreach (products, i, p)
     277              :         {
     278           11 :           if (GNUNET_OK !=
     279           11 :               TALER_MERCHANT_parse_product_sold (p,
     280           11 :                                                  &order->products[i],
     281              :                                                  true))
     282              :           {
     283            0 :             GNUNET_break (0);
     284            0 :             GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     285              :                         "Failed to parse product at offset %u\n",
     286              :                         (unsigned int) i);
     287            0 :             goto cleanup;
     288              :           }
     289              :         }
     290              :       }
     291              :     }
     292              :   }
     293          133 :   switch (order->base->version)
     294              :   {
     295          105 :   case TALER_MERCHANT_CONTRACT_VERSION_0:
     296          105 :     res = parse_order_v0 (input,
     297              :                           order);
     298          105 :     break;
     299           28 :   case TALER_MERCHANT_CONTRACT_VERSION_1:
     300           28 :     res = parse_order_v1 (input,
     301              :                           order);
     302           28 :     break;
     303              :   }
     304              : 
     305          133 :   if (GNUNET_OK != res)
     306              :   {
     307            6 :     GNUNET_break (0);
     308            6 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     309              :                 "Failed to parse order\n");
     310            6 :     goto cleanup;
     311              :   }
     312          127 :   return order;
     313              : 
     314            6 : cleanup:
     315            6 :   TALER_MERCHANT_order_free (order);
     316            6 :   return NULL;
     317              : }
     318              : 
     319              : 
     320              : void
     321          133 : TALER_MERCHANT_order_free (
     322              :   struct TALER_MERCHANT_Order *order)
     323              : {
     324          133 :   if (NULL == order)
     325            0 :     return;
     326          133 :   if (NULL != order->products)
     327              :   {
     328           22 :     for (size_t i = 0; i<order->products_len; i++)
     329           11 :       TALER_MERCHANT_product_sold_free (&order->products[i]);
     330           11 :     GNUNET_free (order->products);
     331           11 :     order->products_len = 0;
     332              :   }
     333          133 :   GNUNET_free (order->order_id);
     334          133 :   if (NULL != order->base)
     335              :   {
     336          133 :     switch (order->base->version)
     337              :     {
     338          105 :     case TALER_MERCHANT_CONTRACT_VERSION_0:
     339          105 :       break;
     340           28 :     case TALER_MERCHANT_CONTRACT_VERSION_1:
     341           28 :       for (unsigned int i = 0;
     342           63 :            i < order->details.v1.choices_len;
     343           35 :            i++)
     344           35 :         TALER_MERCHANT_order_choice_free (&order->details.v1.choices[i]);
     345           28 :       GNUNET_free (order->details.v1.choices);
     346           28 :       break;
     347              :     }
     348          133 :     TALER_MERCHANT_base_terms_free (order->base);
     349          133 :     order->base = NULL;
     350              :   }
     351          133 :   GNUNET_free (order);
     352              : }
        

Generated by: LCOV version 2.0-1