LCOV - code coverage report
Current view: top level - util - product_parse.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 75.0 % 120 90
Test Date: 2026-09-11 17:45:24 Functions: 100.0 % 3 3

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   (C) 2026 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/product_parse.c
      18              :  * @brief small helpers
      19              :  * @author Christian Grothoff
      20              :  */
      21              : #include "platform.h"
      22              : #include <string.h>
      23              : #include "taler/taler_merchant_util.h"
      24              : #include <gnunet/gnunet_json_lib.h>
      25              : #include <taler/taler_json_lib.h>
      26              : 
      27              : /**
      28              :  * Parse the given unit quantity string @a s and store the result in @a q.
      29              :  *
      30              :  * @param s quantity to parse
      31              :  * @param[out] q where to store the result
      32              :  * @return #GNUNET_OK on success
      33              :  */
      34              : static enum GNUNET_GenericReturnValue
      35           25 : parse_unit_quantity (const char *s,
      36              :                      struct TALER_MERCHANT_ProductQuantity *q)
      37              : {
      38              :   const char *ptr;
      39           25 :   uint64_t integer = 0;
      40           25 :   uint32_t frac = 0;
      41           25 :   unsigned int digits = 0;
      42              : 
      43           25 :   if (NULL == s)
      44              :   {
      45            0 :     GNUNET_break_op (0);
      46            0 :     return GNUNET_SYSERR;
      47              :   }
      48           25 :   ptr = s;
      49           25 :   if ('\0' == *ptr)
      50              :   {
      51            0 :     GNUNET_break_op (0);
      52            0 :     return GNUNET_SYSERR;
      53              :   }
      54           25 :   if ('-' == *ptr)
      55              :   {
      56            0 :     GNUNET_break_op (0);
      57            0 :     return GNUNET_SYSERR;
      58              :   }
      59           25 :   if (! isdigit ((unsigned char) *ptr))
      60              :   {
      61            0 :     GNUNET_break_op (0);
      62            0 :     return GNUNET_SYSERR;
      63              :   }
      64           50 :   while (isdigit ((unsigned char) *ptr))
      65              :   {
      66           25 :     unsigned int digit = (unsigned int) (*ptr - '0');
      67              : 
      68              :     /* We intentionally allow at most INT64_MAX (as -1 has special meanings),
      69              :        even though the data type would support UINT64_MAX */
      70           25 :     if (integer > (INT64_MAX - digit) / 10)
      71              :     {
      72            0 :       GNUNET_break_op (0);
      73            0 :       return GNUNET_SYSERR;
      74              :     }
      75           25 :     integer = integer * 10 + digit;
      76           25 :     ptr++;
      77              :   }
      78           25 :   if ('.' == *ptr)
      79              :   {
      80            6 :     ptr++;
      81            6 :     if ('\0' == *ptr)
      82              :     {
      83            0 :       GNUNET_break_op (0);
      84            0 :       return GNUNET_SYSERR;
      85              :     }
      86           12 :     while (isdigit ((unsigned char) *ptr))
      87              :     {
      88            6 :       unsigned int digit = (unsigned int) (*ptr - '0');
      89              : 
      90            6 :       if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
      91              :       {
      92            0 :         GNUNET_break_op (0);
      93            0 :         return GNUNET_SYSERR;
      94              :       }
      95            6 :       frac = (uint32_t) (frac * 10 + digit);
      96            6 :       digits++;
      97            6 :       ptr++;
      98              :     }
      99           36 :     while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
     100              :     {
     101           30 :       frac *= 10;
     102           30 :       digits++;
     103              :     }
     104              :   }
     105           25 :   if ('\0' != *ptr)
     106              :   {
     107            0 :     GNUNET_break_op (0);
     108            0 :     return GNUNET_SYSERR;
     109              :   }
     110           25 :   q->integer = integer;
     111           25 :   q->fractional = frac;
     112           25 :   return GNUNET_OK;
     113              : }
     114              : 
     115              : 
     116              : enum GNUNET_GenericReturnValue
     117           30 : TALER_MERCHANT_parse_product_sold (const json_t *pj,
     118              :                                    struct TALER_MERCHANT_ProductSold *r,
     119              :                                    bool allow_mip)
     120              : {
     121              :   // FIXME: properly implement distinction between MIP and non-MIP!
     122              :   bool no_quantity;
     123              :   bool no_unit_quantity;
     124              :   bool no_price;
     125              :   uint64_t legacy_quantity;
     126              :   const char *unit_quantity_s;
     127              :   struct TALER_Amount price;
     128           30 :   const json_t *prices = NULL;
     129              :   struct GNUNET_JSON_Specification spec[] = {
     130           30 :     GNUNET_JSON_spec_mark_optional (
     131              :       TALER_JSON_spec_slug_copy ("product_id",
     132              :                                  &r->product_id),
     133              :       NULL),
     134           30 :     GNUNET_JSON_spec_mark_optional (
     135              :       GNUNET_JSON_spec_string_copy ("product_name",
     136              :                                     &r->product_name),
     137              :       NULL),
     138           30 :     GNUNET_JSON_spec_mark_optional (
     139              :       GNUNET_JSON_spec_string_copy ("description",
     140              :                                     &r->description),
     141              :       NULL),
     142           30 :     GNUNET_JSON_spec_mark_optional (
     143              :       GNUNET_JSON_spec_object_copy ("description_i18n",
     144              :                                     &r->description_i18n),
     145              :       NULL),
     146           30 :     GNUNET_JSON_spec_mark_optional (
     147              :       GNUNET_JSON_spec_uint64 ("quantity",
     148              :                                &legacy_quantity),
     149              :       &no_quantity),
     150           30 :     GNUNET_JSON_spec_mark_optional (
     151              :       GNUNET_JSON_spec_bool ("prices_are_net",
     152              :                              &r->prices_are_net),
     153              :       NULL),
     154           30 :     GNUNET_JSON_spec_mark_optional (
     155              :       GNUNET_JSON_spec_string ("unit_quantity",
     156              :                                &unit_quantity_s),
     157              :       &no_unit_quantity),
     158           30 :     GNUNET_JSON_spec_mark_optional (
     159              :       TALER_JSON_spec_slug_copy ("unit",
     160              :                                  &r->unit),
     161              :       NULL),
     162           30 :     GNUNET_JSON_spec_mark_optional (
     163              :       TALER_JSON_spec_amount_any ("price",
     164              :                                   &price),
     165              :       &no_price),
     166           30 :     GNUNET_JSON_spec_mark_optional (
     167              :       GNUNET_JSON_spec_array_const ("prices",
     168              :                                     &prices),
     169              :       NULL),
     170           30 :     GNUNET_JSON_spec_mark_optional (
     171              :       GNUNET_JSON_spec_string_copy ("image",
     172              :                                     &r->image),
     173              :       NULL),
     174           30 :     GNUNET_JSON_spec_mark_optional (
     175              :       GNUNET_JSON_spec_array_copy ("taxes",
     176              :                                    &r->taxes),
     177              :       NULL),
     178           30 :     GNUNET_JSON_spec_mark_optional (
     179              :       GNUNET_JSON_spec_timestamp ("delivery_date",
     180              :                                   &r->delivery_date),
     181              :       NULL),
     182           30 :     GNUNET_JSON_spec_mark_optional (
     183              :       GNUNET_JSON_spec_uint64 ("product_money_pot",
     184              :                                &r->product_money_pot),
     185              :       NULL),
     186           30 :     GNUNET_JSON_spec_end ()
     187              :   };
     188              :   enum GNUNET_GenericReturnValue res;
     189              :   const char *ename;
     190              :   unsigned int eline;
     191              : 
     192           30 :   r->delivery_date = GNUNET_TIME_UNIT_FOREVER_TS;
     193           30 :   res = GNUNET_JSON_parse (pj,
     194              :                            spec,
     195              :                            &ename,
     196              :                            &eline);
     197           30 :   if (GNUNET_OK != res)
     198              :   {
     199            0 :     GNUNET_break (0);
     200            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     201              :                 "Failed to parse product at field %s\n",
     202              :                 ename);
     203            0 :     return GNUNET_SYSERR;
     204              :   }
     205           30 :   if (! no_quantity)
     206              :   {
     207           24 :     r->unit_quantity.integer = legacy_quantity;
     208           24 :     r->unit_quantity.fractional = 0;
     209              :   }
     210           30 :   if (! no_unit_quantity)
     211              :   {
     212           25 :     if (GNUNET_OK !=
     213           25 :         parse_unit_quantity (unit_quantity_s,
     214              :                              &r->unit_quantity))
     215              :     {
     216            0 :       GNUNET_break (0);
     217            0 :       return GNUNET_SYSERR;
     218              :     }
     219              :   }
     220           30 :   if ( (! no_quantity) && (! no_unit_quantity) )
     221              :   {
     222           19 :     GNUNET_break ( (0 == r->unit_quantity.fractional) &&
     223              :                    (legacy_quantity == r->unit_quantity.integer) );
     224              :   }
     225           30 :   if ( (NULL != r->image) &&
     226           26 :        (! TALER_MERCHANT_image_data_url_valid (r->image)) )
     227              :   {
     228            0 :     GNUNET_break_op (0);
     229            0 :     return GNUNET_SYSERR;
     230              :   }
     231           30 :   if ( (NULL != r->taxes) &&
     232           26 :        (! TALER_MERCHANT_taxes_array_valid (r->taxes)) )
     233              :   {
     234            0 :     GNUNET_break_op (0);
     235            0 :     return GNUNET_SYSERR;
     236              :   }
     237           55 :   if ( (NULL != prices) &&
     238           25 :        (0 != json_array_size (prices)) )
     239           25 :   {
     240           25 :     size_t len = json_array_size (prices);
     241              :     size_t i;
     242              :     json_t *price_i;
     243              : 
     244           25 :     if (len >= UINT_MAX)
     245              :     {
     246              :       /* 'prices' is untrusted network input; reject rather than abort.
     247              :          (The zero-length case is excluded by the guard above, so we
     248              :          never call GNUNET_new_array(0, ...).) */
     249            0 :       GNUNET_break_op (0);
     250            0 :       return GNUNET_SYSERR;
     251              :     }
     252           25 :     r->prices_length = (unsigned int) len;
     253           25 :     r->prices = GNUNET_new_array (r->prices_length,
     254              :                                   struct TALER_Amount);
     255           50 :     json_array_foreach (prices, i, price_i)
     256              :     {
     257              :       struct GNUNET_JSON_Specification pspec[] = {
     258           25 :         TALER_JSON_spec_amount_any (NULL,
     259           25 :                                     &r->prices[i]),
     260           25 :         GNUNET_JSON_spec_end ()
     261              :       };
     262              : 
     263           25 :       res = GNUNET_JSON_parse (price_i,
     264              :                                pspec,
     265              :                                &ename,
     266              :                                &eline);
     267           25 :       if (GNUNET_OK != res)
     268              :       {
     269            0 :         GNUNET_break (0);
     270            0 :         GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     271              :                     "Failed to parse price at index %u\n",
     272              :                     (unsigned int) i);
     273            0 :         return GNUNET_SYSERR;
     274              :       }
     275              :     }
     276              :   }
     277            5 :   else if (! no_price)
     278              :   {
     279            5 :     r->prices_length = 1;
     280            5 :     r->prices = GNUNET_new_array (1,
     281              :                                   struct TALER_Amount);
     282            5 :     r->prices[0] = price;
     283              :   }
     284           30 :   return GNUNET_OK;
     285              : }
     286              : 
     287              : 
     288              : void
     289           30 : TALER_MERCHANT_product_sold_free (struct TALER_MERCHANT_ProductSold *product)
     290              : {
     291           30 :   GNUNET_free (product->product_id);
     292           30 :   GNUNET_free (product->product_name);
     293           30 :   GNUNET_free (product->description);
     294           30 :   json_decref (product->description_i18n);
     295           30 :   GNUNET_free (product->prices);
     296           30 :   GNUNET_free (product->unit);
     297           30 :   GNUNET_free (product->image);
     298           30 :   json_decref (product->taxes);
     299           30 : }
        

Generated by: LCOV version 2.0-1