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 : }
|