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