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 util/contract_parse.c
18 : * @brief shared logic for contract terms parsing
19 : * @author Iván Ávalos
20 : * @author Christian Grothoff
21 : */
22 : #include "taler/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 merchant details of given JSON contract terms.
35 : *
36 : * @param cls closure, unused parameter
37 : * @param root the JSON object representing data
38 : * @param[out] ospec where to write the data
39 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
40 : */
41 : static enum GNUNET_GenericReturnValue
42 2 : parse_merchant_details (void *cls,
43 : json_t *root,
44 : struct GNUNET_JSON_Specification *ospec)
45 : {
46 2 : struct TALER_MERCHANT_Contract *contract = ospec->ptr;
47 : struct GNUNET_JSON_Specification spec[] = {
48 2 : GNUNET_JSON_spec_string_copy ("name",
49 : &contract->merchant.name),
50 2 : GNUNET_JSON_spec_mark_optional (
51 : GNUNET_JSON_spec_string_copy ("email",
52 : &contract->merchant.email),
53 : NULL),
54 2 : GNUNET_JSON_spec_mark_optional (
55 : GNUNET_JSON_spec_string_copy ("website",
56 : &contract->merchant.website),
57 : NULL),
58 2 : GNUNET_JSON_spec_mark_optional (
59 : GNUNET_JSON_spec_string_copy ("logo",
60 : &contract->merchant.logo),
61 : NULL),
62 2 : GNUNET_JSON_spec_mark_optional (
63 : GNUNET_JSON_spec_object_copy ("address",
64 : &contract->merchant.address),
65 : NULL),
66 2 : GNUNET_JSON_spec_mark_optional (
67 : GNUNET_JSON_spec_object_copy ("jurisdiction",
68 : &contract->merchant.jurisdiction),
69 : NULL),
70 2 : GNUNET_JSON_spec_end ()
71 : };
72 : const char *error_name;
73 : unsigned int error_line;
74 :
75 : (void) cls;
76 2 : if (GNUNET_OK !=
77 2 : GNUNET_JSON_parse (root,
78 : spec,
79 : &error_name,
80 : &error_line))
81 : {
82 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
83 : "Failed to parse %s at %u: %s\n",
84 : spec[error_line].field,
85 : error_line,
86 : error_name);
87 0 : GNUNET_break_op (0);
88 0 : return GNUNET_SYSERR;
89 : }
90 2 : return GNUNET_OK;
91 : }
92 :
93 :
94 : /**
95 : * Provide specification to parse given JSON object to merchant details in the
96 : * contract terms. All fields from @a contract are copied.
97 : *
98 : * @param name name of the merchant details field in the JSON
99 : * @param[out] contract where the merchant details have to be written
100 : */
101 : static struct GNUNET_JSON_Specification
102 2 : spec_merchant_details (const char *name,
103 : struct TALER_MERCHANT_Contract *contract)
104 : {
105 2 : struct GNUNET_JSON_Specification ret = {
106 : .parser = &parse_merchant_details,
107 : .field = name,
108 : .ptr = contract,
109 : };
110 :
111 2 : return ret;
112 : }
113 :
114 :
115 : /**
116 : * Get enum value from contract token type string.
117 : *
118 : * @param str contract token type string
119 : * @return enum value of token type
120 : */
121 : static enum TALER_MERCHANT_ContractTokenKind
122 2 : contract_token_kind_from_string (const char *str)
123 : {
124 2 : if (0 == strcmp ("subscription",
125 : str))
126 1 : return TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION;
127 1 : if (0 == strcmp ("discount",
128 : str))
129 1 : return TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT;
130 0 : return TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID;
131 : }
132 :
133 :
134 : enum GNUNET_GenericReturnValue
135 1 : TALER_MERCHANT_parse_choice_input (
136 : json_t *root,
137 : struct TALER_MERCHANT_ContractInput *input,
138 : size_t index,
139 : bool order)
140 : {
141 : const char *ename;
142 : unsigned int eline;
143 : struct GNUNET_JSON_Specification ispec[] = {
144 1 : TALER_MERCHANT_json_spec_cit ("type",
145 : &input->type),
146 1 : GNUNET_JSON_spec_end ()
147 : };
148 :
149 1 : if (GNUNET_OK !=
150 1 : GNUNET_JSON_parse (root,
151 : ispec,
152 : &ename,
153 : &eline))
154 : {
155 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
156 : "Failed to parse %s at %u: %s\n",
157 : ispec[eline].field,
158 : eline,
159 : ename);
160 0 : GNUNET_break_op (0);
161 0 : return GNUNET_SYSERR;
162 : }
163 :
164 1 : switch (input->type)
165 : {
166 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
167 0 : GNUNET_break (0);
168 0 : break;
169 1 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
170 : {
171 : struct GNUNET_JSON_Specification spec[] = {
172 1 : GNUNET_JSON_spec_string ("token_family_slug",
173 : &input->details.token.token_family_slug),
174 1 : GNUNET_JSON_spec_mark_optional (
175 : GNUNET_JSON_spec_uint32 ("count",
176 1 : &input->details.token.count),
177 : NULL),
178 1 : GNUNET_JSON_spec_end ()
179 : };
180 :
181 1 : if (GNUNET_OK !=
182 1 : GNUNET_JSON_parse (root,
183 : spec,
184 : &ename,
185 : &eline))
186 : {
187 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
188 : "Failed to parse %s at %u: %s\n",
189 : spec[eline].field,
190 : eline,
191 : ename);
192 0 : GNUNET_break_op (0);
193 0 : return GNUNET_SYSERR;
194 : }
195 :
196 1 : return GNUNET_OK;
197 : }
198 : }
199 :
200 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
201 : "Field 'type' invalid in input #%u\n",
202 : (unsigned int) index);
203 0 : GNUNET_break_op (0);
204 0 : return GNUNET_SYSERR;
205 : }
206 :
207 :
208 : enum GNUNET_GenericReturnValue
209 2 : TALER_MERCHANT_parse_choice_output (
210 : json_t *root,
211 : struct TALER_MERCHANT_ContractOutput *output,
212 : size_t index,
213 : bool order)
214 : {
215 : const char *ename;
216 : unsigned int eline;
217 : struct GNUNET_JSON_Specification ispec[] = {
218 2 : TALER_MERCHANT_json_spec_cot ("type",
219 : &output->type),
220 2 : GNUNET_JSON_spec_end ()
221 : };
222 :
223 2 : if (GNUNET_OK !=
224 2 : GNUNET_JSON_parse (root,
225 : ispec,
226 : &ename,
227 : &eline))
228 : {
229 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
230 : "Failed to parse %s at %u: %s\n",
231 : ispec[eline].field,
232 : eline,
233 : ename);
234 0 : GNUNET_break_op (0);
235 0 : return GNUNET_SYSERR;
236 : }
237 :
238 2 : switch (output->type)
239 : {
240 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
241 0 : GNUNET_break (0);
242 0 : break;
243 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
244 : {
245 : struct GNUNET_JSON_Specification spec[] = {
246 1 : GNUNET_JSON_spec_string ("token_family_slug",
247 : &output->details.token.token_family_slug),
248 1 : GNUNET_JSON_spec_mark_optional (
249 : GNUNET_JSON_spec_uint ("count",
250 : &output->details.token.count),
251 : NULL),
252 1 : GNUNET_JSON_spec_mark_optional (
253 : GNUNET_JSON_spec_timestamp ("valid_at",
254 : &output->details.token.valid_at),
255 : NULL),
256 1 : (! order)
257 1 : ? GNUNET_JSON_spec_uint ("key_index",
258 : &output->details.token.key_index)
259 1 : : GNUNET_JSON_spec_end (),
260 1 : GNUNET_JSON_spec_end ()
261 : };
262 :
263 1 : if (GNUNET_OK !=
264 1 : GNUNET_JSON_parse (root,
265 : spec,
266 : &ename,
267 : &eline))
268 : {
269 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
270 : "Failed to parse %s at %u: %s\n",
271 : spec[eline].field,
272 : eline,
273 : ename);
274 0 : GNUNET_break_op (0);
275 0 : return GNUNET_SYSERR;
276 : }
277 :
278 1 : return GNUNET_OK;
279 : }
280 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
281 : {
282 1 : const json_t *donau_urls = NULL;
283 : struct GNUNET_JSON_Specification spec[] = {
284 1 : GNUNET_JSON_spec_mark_optional (
285 : TALER_JSON_spec_amount_any ("amount",
286 : &output->details.donation_receipt.amount),
287 : NULL),
288 1 : (! order)
289 1 : ? GNUNET_JSON_spec_array_const ("donau_urls",
290 : &donau_urls)
291 1 : : GNUNET_JSON_spec_end (),
292 1 : GNUNET_JSON_spec_end ()
293 : };
294 :
295 1 : if (GNUNET_OK !=
296 1 : GNUNET_JSON_parse (root,
297 : spec,
298 : &ename,
299 : &eline))
300 : {
301 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
302 : "Failed to parse %s at %u: %s\n",
303 : spec[eline].field,
304 : eline,
305 : ename);
306 0 : GNUNET_break_op (0);
307 0 : return GNUNET_SYSERR;
308 : }
309 :
310 1 : GNUNET_array_grow (output->details.donation_receipt.donau_urls,
311 : output->details.donation_receipt.donau_urls_len,
312 : json_array_size (donau_urls));
313 :
314 1 : for (unsigned int i = 0;
315 4 : i < output->details.donation_receipt.donau_urls_len;
316 3 : i++)
317 : {
318 : const json_t *jurl;
319 :
320 3 : jurl = json_array_get (donau_urls,
321 : i);
322 3 : if (! json_is_string (jurl))
323 : {
324 0 : GNUNET_break_op (0);
325 0 : return GNUNET_SYSERR;
326 : }
327 3 : output->details.donation_receipt.donau_urls[i] =
328 3 : GNUNET_strdup (json_string_value (jurl));
329 : }
330 :
331 1 : return GNUNET_OK;
332 : }
333 : }
334 :
335 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
336 : "Field 'type' invalid in output #%u\n",
337 : (unsigned int) index);
338 0 : GNUNET_break_op (0);
339 0 : return GNUNET_SYSERR;
340 : }
341 :
342 :
343 : /**
344 : * Parse given JSON object to choices array.
345 : *
346 : * @param cls closure, pointer to array length
347 : * @param root the json array representing the choices
348 : * @param[out] ospec where to write the data
349 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
350 : */
351 : static enum GNUNET_GenericReturnValue
352 1 : parse_choices (
353 : void *cls,
354 : json_t *root,
355 : struct GNUNET_JSON_Specification *ospec)
356 : {
357 1 : struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr;
358 1 : unsigned int *choices_len = cls;
359 :
360 1 : if (! json_is_array (root))
361 : {
362 0 : GNUNET_break_op (0);
363 0 : return GNUNET_SYSERR;
364 : }
365 1 : if (0 == json_array_size (root))
366 : {
367 : /* empty list of choices is not allowed */
368 0 : GNUNET_break_op (0);
369 0 : return GNUNET_SYSERR;
370 : }
371 1 : GNUNET_array_grow (*choices,
372 : *choices_len,
373 : json_array_size (root));
374 :
375 2 : for (unsigned int i = 0; i < *choices_len; i++)
376 : {
377 1 : struct TALER_MERCHANT_ContractChoice *choice = &(*choices)[i];
378 1 : const json_t *jinputs = NULL;
379 1 : const json_t *joutputs = NULL;
380 : struct GNUNET_JSON_Specification spec[] = {
381 1 : TALER_JSON_spec_amount_any ("amount",
382 : &choice->amount),
383 1 : GNUNET_JSON_spec_mark_optional (
384 : TALER_JSON_spec_amount_any ("tip",
385 : &choice->tip),
386 : &choice->no_tip),
387 1 : GNUNET_JSON_spec_mark_optional (
388 : GNUNET_JSON_spec_string_copy ("description",
389 : &choice->description),
390 : NULL),
391 1 : GNUNET_JSON_spec_mark_optional (
392 : GNUNET_JSON_spec_object_copy ("description_i18n",
393 : &choice->description_i18n),
394 : NULL),
395 1 : GNUNET_JSON_spec_mark_optional (
396 : TALER_JSON_spec_amount_any ("max_fee",
397 : &choice->max_fee),
398 : NULL),
399 1 : GNUNET_JSON_spec_mark_optional (
400 : GNUNET_JSON_spec_array_const ("inputs",
401 : &jinputs),
402 : NULL),
403 1 : GNUNET_JSON_spec_mark_optional (
404 : GNUNET_JSON_spec_array_const ("outputs",
405 : &joutputs),
406 : NULL),
407 1 : GNUNET_JSON_spec_end ()
408 : };
409 : const char *ename;
410 : unsigned int eline;
411 :
412 1 : if (GNUNET_OK !=
413 1 : GNUNET_JSON_parse (json_array_get (root, i),
414 : spec,
415 : &ename,
416 : &eline))
417 : {
418 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
419 : "Failed to parse %s at %u: %s\n",
420 : spec[eline].field,
421 : eline,
422 : ename);
423 0 : GNUNET_break_op (0);
424 0 : return GNUNET_SYSERR;
425 : }
426 1 : if ( (! choice->no_tip) &&
427 : (GNUNET_OK !=
428 0 : TALER_amount_cmp_currency (&choice->amount,
429 0 : &choice->tip)) )
430 : {
431 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
432 : "Tip currency does not match amount currency in choice #%u\n",
433 : i);
434 0 : GNUNET_break_op (0);
435 0 : return GNUNET_SYSERR;
436 : }
437 :
438 1 : if (NULL != jinputs)
439 : {
440 : const json_t *jinput;
441 : size_t idx;
442 :
443 2 : json_array_foreach ((json_t *) jinputs, idx, jinput)
444 : {
445 1 : struct TALER_MERCHANT_ContractInput input = {
446 : .details.token.count = 1
447 : };
448 :
449 1 : if (GNUNET_OK !=
450 1 : TALER_MERCHANT_parse_choice_input ((json_t *) jinput,
451 : &input,
452 : idx,
453 : false))
454 : {
455 0 : GNUNET_break (0);
456 0 : return GNUNET_SYSERR;
457 : }
458 1 : switch (input.type)
459 : {
460 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
461 0 : GNUNET_break_op (0);
462 0 : return GNUNET_SYSERR;
463 1 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
464 : /* Ignore inputs tokens with 'count' field set to 0 */
465 1 : if (0 == input.details.token.count)
466 0 : continue;
467 1 : break;
468 : }
469 1 : GNUNET_array_append (choice->inputs,
470 : choice->inputs_len,
471 : input);
472 : }
473 : }
474 :
475 1 : if (NULL != joutputs)
476 : {
477 : const json_t *joutput;
478 : size_t idx;
479 3 : json_array_foreach ((json_t *) joutputs, idx, joutput)
480 : {
481 2 : struct TALER_MERCHANT_ContractOutput output = {
482 : .details.token.count = 1
483 : };
484 :
485 2 : if (GNUNET_OK !=
486 2 : TALER_MERCHANT_parse_choice_output ((json_t *) joutput,
487 : &output,
488 : idx,
489 : false))
490 : {
491 0 : GNUNET_break (0);
492 0 : return GNUNET_SYSERR;
493 : }
494 2 : switch (output.type)
495 : {
496 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
497 0 : GNUNET_break_op (0);
498 0 : return GNUNET_SYSERR;
499 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
500 : /* Ignore output tokens with 'count' field set to 0 */
501 1 : if (0 == output.details.token.count)
502 0 : continue;
503 1 : break;
504 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
505 1 : break;
506 : }
507 2 : GNUNET_array_append (choice->outputs,
508 : choice->outputs_len,
509 : output);
510 : }
511 : }
512 : }
513 :
514 1 : return GNUNET_OK;
515 : }
516 :
517 :
518 : struct GNUNET_JSON_Specification
519 1 : TALER_MERCHANT_spec_choices (
520 : const char *name,
521 : struct TALER_MERCHANT_ContractChoice **choices,
522 : unsigned int *choices_len)
523 : {
524 1 : struct GNUNET_JSON_Specification ret = {
525 : .cls = (void *) choices_len,
526 : .parser = &parse_choices,
527 : .field = name,
528 : .ptr = choices,
529 : };
530 :
531 1 : return ret;
532 : }
533 :
534 :
535 : void
536 1 : TALER_MERCHANT_contract_choice_free (
537 : struct TALER_MERCHANT_ContractChoice *choice)
538 : {
539 3 : for (unsigned int i = 0; i < choice->outputs_len; i++)
540 : {
541 2 : struct TALER_MERCHANT_ContractOutput *output = &choice->outputs[i];
542 :
543 2 : switch (output->type)
544 : {
545 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
546 0 : GNUNET_break (0);
547 0 : break;
548 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
549 1 : break;
550 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
551 1 : for (unsigned int j = 0;
552 4 : j<output->details.donation_receipt.donau_urls_len;
553 3 : j++)
554 3 : GNUNET_free (output->details.donation_receipt.donau_urls[j]);
555 1 : GNUNET_array_grow (output->details.donation_receipt.donau_urls,
556 : output->details.donation_receipt.donau_urls_len,
557 : 0);
558 1 : break;
559 : #if FUTURE
560 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
561 : GNUNET_free (output->details.coin.exchange_url);
562 : break;
563 : #endif
564 : }
565 : }
566 1 : GNUNET_free (choice->description);
567 1 : if (NULL != choice->description_i18n)
568 : {
569 1 : json_decref (choice->description_i18n);
570 1 : choice->description_i18n = NULL;
571 : }
572 1 : GNUNET_free (choice->inputs);
573 1 : GNUNET_free (choice->outputs);
574 1 : }
575 :
576 :
577 : /**
578 : * Parse token details of the given JSON contract token family.
579 : *
580 : * @param cls closure, unused parameter
581 : * @param root the JSON object representing data
582 : * @param[out] ospec where to write the data
583 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
584 : */
585 : static enum GNUNET_GenericReturnValue
586 2 : parse_token_details (void *cls,
587 : json_t *root,
588 : struct GNUNET_JSON_Specification *ospec)
589 : {
590 2 : struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
591 : const char *class;
592 : const char *ename;
593 : unsigned int eline;
594 : struct GNUNET_JSON_Specification ispec[] = {
595 2 : GNUNET_JSON_spec_string ("class",
596 : &class),
597 2 : GNUNET_JSON_spec_end ()
598 : };
599 :
600 : (void) cls;
601 2 : if (GNUNET_OK !=
602 2 : GNUNET_JSON_parse (root,
603 : ispec,
604 : &ename,
605 : &eline))
606 : {
607 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
608 : "Failed to parse %s at %u: %s\n",
609 : ispec[eline].field,
610 : eline,
611 : ename);
612 0 : GNUNET_break_op (0);
613 0 : return GNUNET_SYSERR;
614 : }
615 :
616 2 : family->kind = contract_token_kind_from_string (class);
617 :
618 2 : switch (family->kind)
619 : {
620 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
621 0 : break;
622 1 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
623 : {
624 : const json_t *trusted_domains;
625 : struct GNUNET_JSON_Specification spec[] = {
626 1 : GNUNET_JSON_spec_array_const ("trusted_domains",
627 : &trusted_domains),
628 1 : GNUNET_JSON_spec_end ()
629 : };
630 :
631 1 : if (GNUNET_OK !=
632 1 : GNUNET_JSON_parse (root,
633 : spec,
634 : &ename,
635 : &eline))
636 : {
637 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
638 : "Failed to parse %s at %u: %s\n",
639 : spec[eline].field,
640 : eline,
641 : ename);
642 0 : GNUNET_break_op (0);
643 0 : return GNUNET_SYSERR;
644 : }
645 :
646 1 : GNUNET_array_grow (family->details.subscription.trusted_domains,
647 : family->details.subscription.trusted_domains_len,
648 : json_array_size (trusted_domains));
649 :
650 1 : for (unsigned int i = 0;
651 4 : i < family->details.subscription.trusted_domains_len;
652 3 : i++)
653 : {
654 : const json_t *jdomain;
655 3 : jdomain = json_array_get (trusted_domains, i);
656 :
657 3 : if (! json_is_string (jdomain))
658 : {
659 0 : GNUNET_break_op (0);
660 0 : return GNUNET_SYSERR;
661 : }
662 :
663 3 : family->details.subscription.trusted_domains[i] =
664 3 : GNUNET_strdup (json_string_value (jdomain));
665 : }
666 :
667 1 : return GNUNET_OK;
668 : }
669 1 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
670 : {
671 : const json_t *expected_domains;
672 : struct GNUNET_JSON_Specification spec[] = {
673 1 : GNUNET_JSON_spec_array_const ("expected_domains",
674 : &expected_domains),
675 1 : GNUNET_JSON_spec_end ()
676 : };
677 :
678 1 : if (GNUNET_OK !=
679 1 : GNUNET_JSON_parse (root,
680 : spec,
681 : &ename,
682 : &eline))
683 : {
684 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
685 : "Failed to parse %s at %u: %s\n",
686 : spec[eline].field,
687 : eline,
688 : ename);
689 0 : GNUNET_break_op (0);
690 0 : return GNUNET_SYSERR;
691 : }
692 :
693 1 : GNUNET_array_grow (family->details.discount.expected_domains,
694 : family->details.discount.expected_domains_len,
695 : json_array_size (expected_domains));
696 :
697 1 : for (unsigned int i = 0;
698 4 : i < family->details.discount.expected_domains_len;
699 3 : i++)
700 : {
701 : const json_t *jdomain;
702 :
703 3 : jdomain = json_array_get (expected_domains,
704 : i);
705 3 : if (! json_is_string (jdomain))
706 : {
707 0 : GNUNET_break_op (0);
708 0 : return GNUNET_SYSERR;
709 : }
710 :
711 3 : family->details.discount.expected_domains[i] =
712 3 : GNUNET_strdup (json_string_value (jdomain));
713 : }
714 :
715 1 : return GNUNET_OK;
716 : }
717 : }
718 :
719 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
720 : "Field 'type' invalid in token family\n");
721 0 : GNUNET_break_op (0);
722 0 : return GNUNET_SYSERR;
723 : }
724 :
725 :
726 : /**
727 : * Provide specification to parse given JSON object to contract token details
728 : * in the contract token family. All fields from @a family are copied.
729 : *
730 : * @param name name of the token details field in the JSON
731 : * @param[out] family token_family where the token details have to be written
732 : */
733 : static struct GNUNET_JSON_Specification
734 2 : spec_token_details (const char *name,
735 : struct TALER_MERCHANT_ContractTokenFamily *family)
736 : {
737 2 : struct GNUNET_JSON_Specification ret = {
738 : .parser = &parse_token_details,
739 : .field = name,
740 : .ptr = family,
741 : };
742 :
743 2 : return ret;
744 : }
745 :
746 :
747 : /**
748 : * Parse given JSON object to token families array.
749 : *
750 : * @param cls closure, pointer to array length
751 : * @param root the json object representing the token families. The keys are
752 : * the token family slugs.
753 : * @param[out] ospec where to write the data
754 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
755 : */
756 : static enum GNUNET_GenericReturnValue
757 1 : parse_token_families (void *cls,
758 : json_t *root,
759 : struct GNUNET_JSON_Specification *ospec)
760 : {
761 1 : struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
762 1 : unsigned int *families_len = cls;
763 : json_t *jfamily;
764 : const char *slug;
765 :
766 1 : if (! json_is_object (root))
767 : {
768 0 : GNUNET_break_op (0);
769 0 : return GNUNET_SYSERR;
770 : }
771 :
772 3 : json_object_foreach (root, slug, jfamily)
773 : {
774 : const json_t *keys;
775 4 : struct TALER_MERCHANT_ContractTokenFamily family = {
776 2 : .slug = GNUNET_strdup (slug)
777 : };
778 : struct GNUNET_JSON_Specification spec[] = {
779 2 : GNUNET_JSON_spec_string_copy ("name",
780 : &family.name),
781 2 : GNUNET_JSON_spec_string_copy ("description",
782 : &family.description),
783 2 : GNUNET_JSON_spec_object_copy ("description_i18n",
784 : &family.description_i18n),
785 2 : GNUNET_JSON_spec_array_const ("keys",
786 : &keys),
787 2 : spec_token_details ("details",
788 : &family),
789 2 : GNUNET_JSON_spec_bool ("critical",
790 : &family.critical),
791 2 : GNUNET_JSON_spec_end ()
792 : };
793 : const char *error_name;
794 : unsigned int error_line;
795 :
796 2 : if (GNUNET_OK !=
797 2 : GNUNET_JSON_parse (jfamily,
798 : spec,
799 : &error_name,
800 : &error_line))
801 : {
802 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
803 : "Failed to parse %s at %u: %s\n",
804 : spec[error_line].field,
805 : error_line,
806 : error_name);
807 0 : GNUNET_break_op (0);
808 0 : return GNUNET_SYSERR;
809 : }
810 :
811 2 : GNUNET_array_grow (family.keys,
812 : family.keys_len,
813 : json_array_size (keys));
814 :
815 4 : for (unsigned int i = 0; i<family.keys_len; i++)
816 : {
817 2 : struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
818 : struct GNUNET_JSON_Specification key_spec[] = {
819 2 : TALER_JSON_spec_token_pub (
820 : NULL,
821 : &key->pub),
822 2 : GNUNET_JSON_spec_timestamp (
823 : "signature_validity_start",
824 : &key->valid_after),
825 2 : GNUNET_JSON_spec_timestamp (
826 : "signature_validity_end",
827 : &key->valid_before),
828 2 : GNUNET_JSON_spec_end ()
829 : };
830 : const char *ierror_name;
831 : unsigned int ierror_line;
832 :
833 2 : if (GNUNET_OK !=
834 2 : GNUNET_JSON_parse (json_array_get (keys,
835 : i),
836 : key_spec,
837 : &ierror_name,
838 : &ierror_line))
839 : {
840 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
841 : "Failed to parse %s at %u: %s\n",
842 : key_spec[ierror_line].field,
843 : ierror_line,
844 : ierror_name);
845 0 : GNUNET_break_op (0);
846 0 : return GNUNET_SYSERR;
847 : }
848 : }
849 :
850 2 : GNUNET_array_append (*families,
851 : *families_len,
852 : family);
853 : }
854 :
855 1 : return GNUNET_OK;
856 : }
857 :
858 :
859 : /**
860 : * Provide specification to parse given JSON array to token families in the
861 : * contract terms. All fields from @a families items are copied.
862 : *
863 : * @param name name of the token families field in the JSON
864 : * @param[out] families where the token families array has to be written
865 : * @param[out] families_len length of the @a families array
866 : */
867 : static struct GNUNET_JSON_Specification
868 1 : spec_token_families (
869 : const char *name,
870 : struct TALER_MERCHANT_ContractTokenFamily **families,
871 : unsigned int *families_len)
872 : {
873 1 : struct GNUNET_JSON_Specification ret = {
874 : .cls = (void *) families_len,
875 : .parser = &parse_token_families,
876 : .field = name,
877 : .ptr = families,
878 : };
879 :
880 1 : return ret;
881 : }
882 :
883 :
884 : enum GNUNET_GenericReturnValue
885 2 : TALER_MERCHANT_find_token_family_key (
886 : const char *slug,
887 : struct GNUNET_TIME_Timestamp valid_after,
888 : const struct TALER_MERCHANT_ContractTokenFamily *families,
889 : unsigned int families_len,
890 : struct TALER_MERCHANT_ContractTokenFamily *family,
891 : struct TALER_MERCHANT_ContractTokenFamilyKey *key)
892 : {
893 3 : for (unsigned int i = 0; i < families_len; i++)
894 : {
895 3 : const struct TALER_MERCHANT_ContractTokenFamily *fami
896 3 : = &families[i];
897 :
898 3 : if (0 != strcmp (fami->slug,
899 : slug))
900 1 : continue;
901 2 : if (NULL != family)
902 2 : *family = *fami;
903 2 : for (unsigned int k = 0; k < fami->keys_len; k++)
904 : {
905 2 : struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
906 :
907 2 : if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
908 : ==,
909 : valid_after))
910 : {
911 2 : if (NULL != key)
912 2 : *key = *ki;
913 2 : return GNUNET_OK;
914 : }
915 : }
916 : /* matching family found, but no key. */
917 0 : return GNUNET_NO;
918 : }
919 :
920 : /* no matching family found */
921 0 : return GNUNET_SYSERR;
922 : }
923 :
924 :
925 : /**
926 : * Free all the fields in the given @a family, but not @a family itself, since
927 : * it is normally part of an array.
928 : *
929 : * @param[in] family contract token family to free
930 : */
931 : static void
932 2 : contract_token_family_free (
933 : struct TALER_MERCHANT_ContractTokenFamily *family)
934 : {
935 2 : GNUNET_free (family->slug);
936 2 : GNUNET_free (family->name);
937 2 : GNUNET_free (family->description);
938 2 : if (NULL != family->description_i18n)
939 : {
940 2 : json_decref (family->description_i18n);
941 2 : family->description_i18n = NULL;
942 : }
943 4 : for (unsigned int i = 0; i < family->keys_len; i++)
944 2 : TALER_token_issue_pub_free (&family->keys[i].pub);
945 2 : GNUNET_free (family->keys);
946 :
947 2 : switch (family->kind)
948 : {
949 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
950 0 : break;
951 1 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
952 4 : for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
953 3 : i++)
954 3 : GNUNET_free (family->details.discount.expected_domains[i]);
955 1 : break;
956 1 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
957 4 : for (unsigned int i = 0; i < family->details.subscription.
958 3 : trusted_domains_len; i++)
959 3 : GNUNET_free (family->details.subscription.trusted_domains[i]);
960 1 : break;
961 : }
962 2 : }
963 :
964 :
965 : /**
966 : * Parse contract version of given JSON contract terms.
967 : *
968 : * @param cls closure, unused parameter
969 : * @param root the JSON object representing data
970 : * @param[out] spec where to write the data
971 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
972 : */
973 : static enum GNUNET_GenericReturnValue
974 2 : parse_contract_version (void *cls,
975 : json_t *root,
976 : struct GNUNET_JSON_Specification *spec)
977 : {
978 2 : enum TALER_MERCHANT_ContractVersion *res
979 : = (enum TALER_MERCHANT_ContractVersion *) spec->ptr;
980 :
981 : (void) cls;
982 2 : if (json_is_integer (root))
983 : {
984 2 : json_int_t version = json_integer_value (root);
985 :
986 2 : switch (version)
987 : {
988 1 : case 0:
989 1 : *res = TALER_MERCHANT_CONTRACT_VERSION_0;
990 1 : return GNUNET_OK;
991 1 : case 1:
992 1 : *res = TALER_MERCHANT_CONTRACT_VERSION_1;
993 1 : return GNUNET_OK;
994 : }
995 :
996 0 : GNUNET_break_op (0);
997 0 : return GNUNET_SYSERR;
998 : }
999 :
1000 0 : if (json_is_null (root))
1001 : {
1002 0 : *res = TALER_MERCHANT_CONTRACT_VERSION_0;
1003 0 : return GNUNET_OK;
1004 : }
1005 :
1006 0 : GNUNET_break_op (0);
1007 0 : return GNUNET_SYSERR;
1008 : }
1009 :
1010 :
1011 : /**
1012 : * Create JSON specification to parse a merchant contract
1013 : * version.
1014 : *
1015 : * @param name name of the field
1016 : * @param[out] version where to write the contract version
1017 : * @return JSON specification object
1018 : */
1019 : static struct GNUNET_JSON_Specification
1020 2 : spec_contract_version (
1021 : const char *name,
1022 : enum TALER_MERCHANT_ContractVersion *version)
1023 : {
1024 2 : struct GNUNET_JSON_Specification ret = {
1025 : .parser = &parse_contract_version,
1026 : .field = name,
1027 : .ptr = version
1028 : };
1029 :
1030 2 : *version = TALER_MERCHANT_CONTRACT_VERSION_0;
1031 2 : return ret;
1032 : }
1033 :
1034 :
1035 : /**
1036 : * Parse v0-specific fields of @a input JSON into @a contract.
1037 : *
1038 : * @param[in] input the JSON contract terms
1039 : * @param[out] contract where to write the data
1040 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1041 : */
1042 : static enum GNUNET_GenericReturnValue
1043 1 : parse_contract_v0 (
1044 : json_t *input,
1045 : struct TALER_MERCHANT_Contract *contract)
1046 : {
1047 : struct GNUNET_JSON_Specification espec[] = {
1048 1 : TALER_JSON_spec_amount_any ("amount",
1049 : &contract->details.v0.brutto),
1050 1 : GNUNET_JSON_spec_mark_optional (
1051 : TALER_JSON_spec_amount_any ("tip",
1052 : &contract->details.v0.tip),
1053 : &contract->details.v0.no_tip),
1054 1 : TALER_JSON_spec_amount_any ("max_fee",
1055 : &contract->details.v0.max_fee),
1056 1 : GNUNET_JSON_spec_end ()
1057 : };
1058 : enum GNUNET_GenericReturnValue res;
1059 : const char *ename;
1060 : unsigned int eline;
1061 :
1062 1 : res = GNUNET_JSON_parse (input,
1063 : espec,
1064 : &ename,
1065 : &eline);
1066 1 : if (GNUNET_OK != res)
1067 : {
1068 0 : GNUNET_break (0);
1069 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1070 : "Failed to parse contract v0 at field %s\n",
1071 : ename);
1072 0 : return GNUNET_SYSERR;
1073 : }
1074 :
1075 1 : if (GNUNET_OK !=
1076 1 : TALER_amount_cmp_currency (&contract->details.v0.max_fee,
1077 1 : &contract->details.v0.brutto))
1078 : {
1079 0 : GNUNET_break (0);
1080 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1081 : "'max_fee' in database does not match currency of contract price");
1082 0 : return GNUNET_SYSERR;
1083 : }
1084 1 : if ( (! contract->details.v0.no_tip) &&
1085 : (GNUNET_OK !=
1086 0 : TALER_amount_cmp_currency (&contract->details.v0.tip,
1087 0 : &contract->details.v0.brutto)) )
1088 : {
1089 0 : GNUNET_break (0);
1090 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1091 : "'tip' in database does not match currency of contract price");
1092 0 : return GNUNET_SYSERR;
1093 : }
1094 :
1095 1 : return res;
1096 : }
1097 :
1098 :
1099 : /**
1100 : * Parse v1-specific fields of @a input JSON into @a contract.
1101 : *
1102 : * @param[in] input the JSON contract terms
1103 : * @param[out] contract where to write the data
1104 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1105 : */
1106 : static enum GNUNET_GenericReturnValue
1107 1 : parse_contract_v1 (
1108 : json_t *input,
1109 : struct TALER_MERCHANT_Contract *contract)
1110 : {
1111 : struct GNUNET_JSON_Specification espec[] = {
1112 1 : TALER_MERCHANT_spec_choices (
1113 : "choices",
1114 : &contract->details.v1.choices,
1115 : &contract->details.v1.choices_len),
1116 1 : spec_token_families (
1117 : "token_families",
1118 : &contract->details.v1.token_authorities,
1119 : &contract->details.v1.token_authorities_len),
1120 1 : GNUNET_JSON_spec_end ()
1121 : };
1122 :
1123 : enum GNUNET_GenericReturnValue res;
1124 : const char *ename;
1125 : unsigned int eline;
1126 :
1127 1 : res = GNUNET_JSON_parse (input,
1128 : espec,
1129 : &ename,
1130 : &eline);
1131 1 : if (GNUNET_OK != res)
1132 : {
1133 0 : GNUNET_break (0);
1134 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1135 : "Failed to parse contract v1 at field %s\n",
1136 : ename);
1137 0 : return GNUNET_SYSERR;
1138 : }
1139 :
1140 1 : return res;
1141 : }
1142 :
1143 :
1144 : /**
1145 : * Parse the given unit quantity string @a s and store the result in @a q.
1146 : *
1147 : * @param s quantity to parse
1148 : * @param[out] q where to store the result
1149 : * @return #GNUNET_OK on success
1150 : */
1151 : static enum GNUNET_GenericReturnValue
1152 0 : parse_unit_quantity (const char *s,
1153 : struct TALER_MERCHANT_ProductQuantity *q)
1154 : {
1155 : const char *ptr;
1156 0 : uint64_t integer = 0;
1157 0 : uint32_t frac = 0;
1158 0 : unsigned int digits = 0;
1159 :
1160 0 : if (NULL == s)
1161 : {
1162 0 : GNUNET_break_op (0);
1163 0 : return GNUNET_SYSERR;
1164 : }
1165 0 : ptr = s;
1166 0 : if ('\0' == *ptr)
1167 : {
1168 0 : GNUNET_break_op (0);
1169 0 : return GNUNET_SYSERR;
1170 : }
1171 0 : if ('-' == *ptr)
1172 : {
1173 0 : GNUNET_break_op (0);
1174 0 : return GNUNET_SYSERR;
1175 : }
1176 0 : if (! isdigit ((unsigned char) *ptr))
1177 : {
1178 0 : GNUNET_break_op (0);
1179 0 : return GNUNET_SYSERR;
1180 : }
1181 0 : while (isdigit ((unsigned char) *ptr))
1182 : {
1183 0 : unsigned int digit = (unsigned int) (*ptr - '0');
1184 :
1185 : /* We intentionally allow at most INT64_MAX (as -1 has special meanings),
1186 : even though the data type would support UINT64_MAX */
1187 0 : if (integer > (INT64_MAX - digit) / 10)
1188 : {
1189 0 : GNUNET_break_op (0);
1190 0 : return GNUNET_SYSERR;
1191 : }
1192 0 : integer = integer * 10 + digit;
1193 0 : ptr++;
1194 : }
1195 0 : if ('.' == *ptr)
1196 : {
1197 0 : ptr++;
1198 0 : if ('\0' == *ptr)
1199 : {
1200 0 : GNUNET_break_op (0);
1201 0 : return GNUNET_SYSERR;
1202 : }
1203 0 : while (isdigit ((unsigned char) *ptr))
1204 : {
1205 0 : unsigned int digit = (unsigned int) (*ptr - '0');
1206 :
1207 0 : if (digits >= TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
1208 : {
1209 0 : GNUNET_break_op (0);
1210 0 : return GNUNET_SYSERR;
1211 : }
1212 0 : frac = (uint32_t) (frac * 10 + digit);
1213 0 : digits++;
1214 0 : ptr++;
1215 : }
1216 0 : while (digits < TALER_MERCHANT_UNIT_FRAC_MAX_DIGITS)
1217 : {
1218 0 : frac *= 10;
1219 0 : digits++;
1220 : }
1221 : }
1222 0 : if ('\0' != *ptr)
1223 : {
1224 0 : GNUNET_break_op (0);
1225 0 : return GNUNET_SYSERR;
1226 : }
1227 0 : q->integer = integer;
1228 0 : q->fractional = frac;
1229 0 : return GNUNET_OK;
1230 : }
1231 :
1232 :
1233 : bool
1234 0 : TALER_MERCHANT_taxes_array_valid (const json_t *taxes)
1235 : {
1236 : json_t *tax;
1237 : size_t idx;
1238 :
1239 0 : if (! json_is_array (taxes))
1240 0 : return false;
1241 0 : json_array_foreach (taxes, idx, tax)
1242 : {
1243 : struct TALER_Amount amount;
1244 : const char *name;
1245 : struct GNUNET_JSON_Specification spec[] = {
1246 0 : GNUNET_JSON_spec_string ("name",
1247 : &name),
1248 0 : TALER_JSON_spec_amount_any ("tax",
1249 : &amount),
1250 0 : GNUNET_JSON_spec_end ()
1251 : };
1252 : enum GNUNET_GenericReturnValue res;
1253 : const char *ename;
1254 : unsigned int eline;
1255 :
1256 0 : res = GNUNET_JSON_parse (tax,
1257 : spec,
1258 : &ename,
1259 : &eline);
1260 0 : if (GNUNET_OK != res)
1261 : {
1262 0 : GNUNET_break_op (0);
1263 0 : return false;
1264 : }
1265 : }
1266 0 : return true;
1267 : }
1268 :
1269 :
1270 : enum GNUNET_GenericReturnValue
1271 0 : TALER_MERCHANT_parse_product_sold (const json_t *pj,
1272 : struct TALER_MERCHANT_ProductSold *r)
1273 : {
1274 : bool no_quantity;
1275 : bool no_unit_quantity;
1276 : bool no_price;
1277 : uint64_t legacy_quantity;
1278 : const char *unit_quantity_s;
1279 : struct TALER_Amount price;
1280 0 : const json_t *prices = NULL;
1281 : struct GNUNET_JSON_Specification spec[] = {
1282 0 : GNUNET_JSON_spec_mark_optional (
1283 : GNUNET_JSON_spec_string_copy ("product_id",
1284 : &r->product_id),
1285 : NULL),
1286 0 : GNUNET_JSON_spec_mark_optional (
1287 : GNUNET_JSON_spec_string_copy ("product_name",
1288 : &r->product_name),
1289 : NULL),
1290 0 : GNUNET_JSON_spec_mark_optional (
1291 : GNUNET_JSON_spec_string_copy ("description",
1292 : &r->description),
1293 : NULL),
1294 0 : GNUNET_JSON_spec_mark_optional (
1295 : GNUNET_JSON_spec_object_copy ("description_i18n",
1296 : &r->description_i18n),
1297 : NULL),
1298 0 : GNUNET_JSON_spec_mark_optional (
1299 : GNUNET_JSON_spec_uint64 ("quantity",
1300 : &legacy_quantity),
1301 : &no_quantity),
1302 0 : GNUNET_JSON_spec_mark_optional (
1303 : GNUNET_JSON_spec_string ("unit_quantity",
1304 : &unit_quantity_s),
1305 : &no_unit_quantity),
1306 0 : GNUNET_JSON_spec_mark_optional (
1307 : GNUNET_JSON_spec_string_copy ("unit",
1308 : &r->unit),
1309 : NULL),
1310 0 : GNUNET_JSON_spec_mark_optional (
1311 : TALER_JSON_spec_amount_any ("price",
1312 : &price),
1313 : &no_price),
1314 0 : GNUNET_JSON_spec_mark_optional (
1315 : GNUNET_JSON_spec_array_const ("prices",
1316 : &prices),
1317 : NULL),
1318 0 : GNUNET_JSON_spec_mark_optional (
1319 : GNUNET_JSON_spec_string_copy ("image",
1320 : &r->image),
1321 : NULL),
1322 0 : GNUNET_JSON_spec_mark_optional (
1323 : GNUNET_JSON_spec_array_copy ("taxes",
1324 : &r->taxes),
1325 : NULL),
1326 0 : GNUNET_JSON_spec_mark_optional (
1327 : GNUNET_JSON_spec_timestamp ("delivery_date",
1328 : &r->delivery_date),
1329 : NULL),
1330 0 : GNUNET_JSON_spec_mark_optional (
1331 : GNUNET_JSON_spec_uint64 ("product_money_pot",
1332 : &r->product_money_pot),
1333 : NULL),
1334 0 : GNUNET_JSON_spec_end ()
1335 : };
1336 : enum GNUNET_GenericReturnValue res;
1337 : const char *ename;
1338 : unsigned int eline;
1339 :
1340 0 : r->delivery_date = GNUNET_TIME_UNIT_FOREVER_TS;
1341 0 : res = GNUNET_JSON_parse (pj,
1342 : spec,
1343 : &ename,
1344 : &eline);
1345 0 : if (GNUNET_OK != res)
1346 : {
1347 0 : GNUNET_break (0);
1348 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1349 : "Failed to parse product at field %s\n",
1350 : ename);
1351 0 : return GNUNET_SYSERR;
1352 : }
1353 0 : if (! no_quantity)
1354 : {
1355 0 : r->unit_quantity.integer = legacy_quantity;
1356 0 : r->unit_quantity.fractional = 0;
1357 : }
1358 0 : if (! no_unit_quantity)
1359 : {
1360 0 : if (GNUNET_OK !=
1361 0 : parse_unit_quantity (unit_quantity_s,
1362 : &r->unit_quantity))
1363 : {
1364 0 : GNUNET_break (0);
1365 0 : return GNUNET_SYSERR;
1366 : }
1367 : }
1368 0 : if ( (! no_quantity) && (! no_unit_quantity) )
1369 : {
1370 0 : GNUNET_break ( (0 == r->unit_quantity.fractional) &&
1371 : (legacy_quantity == r->unit_quantity.integer) );
1372 : }
1373 0 : if ( (NULL != r->image) &&
1374 0 : (! TALER_MERCHANT_image_data_url_valid (r->image)) )
1375 : {
1376 0 : GNUNET_break_op (0);
1377 0 : return GNUNET_SYSERR;
1378 : }
1379 0 : if ( (NULL != r->taxes) &&
1380 0 : (! TALER_MERCHANT_taxes_array_valid (r->taxes)) )
1381 : {
1382 0 : GNUNET_break_op (0);
1383 0 : return GNUNET_SYSERR;
1384 : }
1385 0 : if (NULL != prices)
1386 : {
1387 0 : size_t len = json_array_size (prices);
1388 : size_t i;
1389 : json_t *price_i;
1390 :
1391 0 : GNUNET_assert (len < UINT_MAX);
1392 0 : r->prices = GNUNET_new_array ((unsigned int) len,
1393 : struct TALER_Amount);
1394 0 : json_array_foreach (prices, i, price_i)
1395 : {
1396 : struct GNUNET_JSON_Specification pspec[] = {
1397 0 : TALER_JSON_spec_amount_any (NULL,
1398 0 : &r->prices[i]),
1399 0 : GNUNET_JSON_spec_end ()
1400 : };
1401 :
1402 0 : res = GNUNET_JSON_parse (price_i,
1403 : pspec,
1404 : &ename,
1405 : &eline);
1406 0 : if (GNUNET_OK != res)
1407 : {
1408 0 : GNUNET_break (0);
1409 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1410 : "Failed to parse price at index %u\n",
1411 : (unsigned int) i);
1412 0 : return GNUNET_SYSERR;
1413 : }
1414 : }
1415 : }
1416 0 : else if (! no_price)
1417 : {
1418 0 : r->prices = GNUNET_new_array (1,
1419 : struct TALER_Amount);
1420 0 : r->prices[0] = price;
1421 : }
1422 0 : return GNUNET_OK;
1423 : }
1424 :
1425 :
1426 : struct TALER_MERCHANT_Contract *
1427 2 : TALER_MERCHANT_contract_parse (json_t *input,
1428 : bool nonce_optional)
1429 : {
1430 : struct TALER_MERCHANT_Contract *contract
1431 2 : = GNUNET_new (struct TALER_MERCHANT_Contract);
1432 2 : const json_t *products = NULL;
1433 : struct GNUNET_JSON_Specification espec[] = {
1434 2 : spec_contract_version ("version",
1435 : &contract->version),
1436 2 : GNUNET_JSON_spec_string_copy ("summary",
1437 : &contract->summary),
1438 : /* FIXME: do i18n_str validation in the future */
1439 2 : GNUNET_JSON_spec_mark_optional (
1440 : GNUNET_JSON_spec_object_copy ("summary_i18n",
1441 : &contract->summary_i18n),
1442 : NULL),
1443 2 : GNUNET_JSON_spec_string_copy ("order_id",
1444 : &contract->order_id),
1445 2 : GNUNET_JSON_spec_mark_optional (
1446 : GNUNET_JSON_spec_string_copy ("public_reorder_url",
1447 : &contract->public_reorder_url),
1448 : NULL),
1449 2 : GNUNET_JSON_spec_mark_optional (
1450 : GNUNET_JSON_spec_string_copy ("fulfillment_url",
1451 : &contract->fulfillment_url),
1452 : NULL),
1453 2 : GNUNET_JSON_spec_mark_optional (
1454 : GNUNET_JSON_spec_string_copy ("fulfillment_message",
1455 : &contract->fulfillment_message),
1456 : NULL),
1457 2 : GNUNET_JSON_spec_mark_optional (
1458 : GNUNET_JSON_spec_object_copy ("fulfillment_message_i18n",
1459 : &contract->fulfillment_message_i18n),
1460 : NULL),
1461 2 : GNUNET_JSON_spec_mark_optional (
1462 : GNUNET_JSON_spec_array_const ("products",
1463 : &products),
1464 : NULL),
1465 2 : GNUNET_JSON_spec_timestamp ("timestamp",
1466 : &contract->timestamp),
1467 2 : GNUNET_JSON_spec_timestamp ("refund_deadline",
1468 : &contract->refund_deadline),
1469 2 : GNUNET_JSON_spec_timestamp ("pay_deadline",
1470 : &contract->pay_deadline),
1471 2 : GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
1472 : &contract->wire_deadline),
1473 2 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
1474 : &contract->merchant_pub),
1475 2 : GNUNET_JSON_spec_string_copy ("merchant_base_url",
1476 : &contract->merchant_base_url),
1477 2 : spec_merchant_details ("merchant",
1478 : contract),
1479 2 : GNUNET_JSON_spec_fixed_auto ("h_wire",
1480 : &contract->h_wire),
1481 2 : GNUNET_JSON_spec_string_copy ("wire_method",
1482 : &contract->wire_method),
1483 2 : GNUNET_JSON_spec_array_copy ("exchanges",
1484 : &contract->exchanges),
1485 2 : GNUNET_JSON_spec_mark_optional (
1486 : GNUNET_JSON_spec_object_copy ("delivery_location",
1487 : &contract->delivery_location),
1488 : NULL),
1489 2 : GNUNET_JSON_spec_mark_optional (
1490 : GNUNET_JSON_spec_timestamp ("delivery_date",
1491 : &contract->delivery_date),
1492 : NULL),
1493 : (nonce_optional)
1494 0 : ? GNUNET_JSON_spec_mark_optional (
1495 : GNUNET_JSON_spec_string_copy ("nonce",
1496 : &contract->nonce),
1497 : NULL)
1498 2 : : GNUNET_JSON_spec_string_copy ("nonce",
1499 : &contract->nonce),
1500 2 : GNUNET_JSON_spec_mark_optional (
1501 : GNUNET_JSON_spec_relative_time ("auto_refund",
1502 : &contract->auto_refund),
1503 : NULL),
1504 2 : GNUNET_JSON_spec_mark_optional (
1505 : GNUNET_JSON_spec_object_copy ("extra",
1506 : &contract->extra),
1507 : NULL),
1508 2 : GNUNET_JSON_spec_mark_optional (
1509 : GNUNET_JSON_spec_uint8 ("minimum_age",
1510 : &contract->minimum_age),
1511 : NULL),
1512 2 : GNUNET_JSON_spec_mark_optional (
1513 : GNUNET_JSON_spec_uint64 ("default_money_pot",
1514 : &contract->default_money_pot),
1515 : NULL),
1516 2 : GNUNET_JSON_spec_end ()
1517 : };
1518 :
1519 : enum GNUNET_GenericReturnValue res;
1520 : const char *ename;
1521 : unsigned int eline;
1522 :
1523 2 : GNUNET_assert (NULL != input);
1524 2 : res = GNUNET_JSON_parse (input,
1525 : espec,
1526 : &ename,
1527 : &eline);
1528 2 : if (GNUNET_OK != res)
1529 : {
1530 0 : GNUNET_break (0);
1531 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1532 : "Failed to parse contract at field %s\n",
1533 : ename);
1534 0 : goto cleanup;
1535 : }
1536 2 : if (NULL != products)
1537 : {
1538 2 : contract->products_len = json_array_size (products);
1539 2 : if (0 != contract->products_len)
1540 : {
1541 : size_t i;
1542 : json_t *p;
1543 :
1544 0 : contract->products = GNUNET_new_array (contract->products_len,
1545 : struct TALER_MERCHANT_ProductSold);
1546 0 : json_array_foreach (products, i, p)
1547 : {
1548 0 : if (GNUNET_OK !=
1549 0 : TALER_MERCHANT_parse_product_sold (p,
1550 0 : &contract->products[i]))
1551 : {
1552 0 : GNUNET_break (0);
1553 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1554 : "Failed to parse product at offset %u\n",
1555 : (unsigned int) i);
1556 0 : goto cleanup;
1557 : }
1558 : }
1559 : }
1560 : }
1561 2 : switch (contract->version)
1562 : {
1563 1 : case TALER_MERCHANT_CONTRACT_VERSION_0:
1564 1 : res = parse_contract_v0 (input,
1565 : contract);
1566 1 : break;
1567 1 : case TALER_MERCHANT_CONTRACT_VERSION_1:
1568 1 : res = parse_contract_v1 (input,
1569 : contract);
1570 1 : break;
1571 : }
1572 :
1573 2 : if (GNUNET_OK != res)
1574 : {
1575 0 : GNUNET_break (0);
1576 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1577 : "Failed to parse contract\n");
1578 0 : goto cleanup;
1579 : }
1580 2 : return contract;
1581 :
1582 0 : cleanup:
1583 0 : TALER_MERCHANT_contract_free (contract);
1584 0 : return NULL;
1585 : }
1586 :
1587 :
1588 : void
1589 0 : TALER_MERCHANT_product_sold_free (struct TALER_MERCHANT_ProductSold *product)
1590 : {
1591 0 : GNUNET_free (product->product_id);
1592 0 : GNUNET_free (product->product_name);
1593 0 : GNUNET_free (product->description);
1594 0 : json_decref (product->description_i18n);
1595 0 : GNUNET_free (product->prices);
1596 0 : GNUNET_free (product->unit);
1597 0 : GNUNET_free (product->image);
1598 0 : json_decref (product->taxes);
1599 0 : }
1600 :
1601 :
1602 : void
1603 2 : TALER_MERCHANT_contract_free (
1604 : struct TALER_MERCHANT_Contract *contract)
1605 : {
1606 2 : if (NULL == contract)
1607 0 : return;
1608 2 : GNUNET_free (contract->public_reorder_url);
1609 2 : GNUNET_free (contract->order_id);
1610 2 : GNUNET_free (contract->merchant_base_url);
1611 2 : GNUNET_free (contract->merchant.name);
1612 2 : GNUNET_free (contract->merchant.website);
1613 2 : GNUNET_free (contract->merchant.email);
1614 2 : GNUNET_free (contract->merchant.logo);
1615 2 : if (NULL != contract->merchant.address)
1616 : {
1617 2 : json_decref (contract->merchant.address);
1618 2 : contract->merchant.address = NULL;
1619 : }
1620 2 : if (NULL != contract->merchant.jurisdiction)
1621 : {
1622 2 : json_decref (contract->merchant.jurisdiction);
1623 2 : contract->merchant.jurisdiction = NULL;
1624 : }
1625 2 : GNUNET_free (contract->summary);
1626 2 : GNUNET_free (contract->fulfillment_url);
1627 2 : GNUNET_free (contract->fulfillment_message);
1628 2 : if (NULL != contract->fulfillment_message_i18n)
1629 : {
1630 2 : json_decref (contract->fulfillment_message_i18n);
1631 2 : contract->fulfillment_message_i18n = NULL;
1632 : }
1633 2 : if (NULL != contract->products)
1634 : {
1635 0 : for (size_t i = 0; i<contract->products_len; i++)
1636 0 : TALER_MERCHANT_product_sold_free (&contract->products[i]);
1637 0 : GNUNET_free (contract->products);
1638 : }
1639 2 : GNUNET_free (contract->wire_method);
1640 2 : if (NULL != contract->exchanges)
1641 : {
1642 2 : json_decref (contract->exchanges);
1643 2 : contract->exchanges = NULL;
1644 : }
1645 2 : if (NULL != contract->delivery_location)
1646 : {
1647 2 : json_decref (contract->delivery_location);
1648 2 : contract->delivery_location = NULL;
1649 : }
1650 2 : GNUNET_free (contract->nonce);
1651 2 : if (NULL != contract->extra)
1652 : {
1653 2 : json_decref (contract->extra);
1654 2 : contract->extra = NULL;
1655 : }
1656 :
1657 2 : switch (contract->version)
1658 : {
1659 1 : case TALER_MERCHANT_CONTRACT_VERSION_0:
1660 1 : break;
1661 1 : case TALER_MERCHANT_CONTRACT_VERSION_1:
1662 1 : for (unsigned int i = 0;
1663 2 : i < contract->details.v1.choices_len;
1664 1 : i++)
1665 1 : TALER_MERCHANT_contract_choice_free (&contract->details.v1.choices[i]);
1666 1 : GNUNET_free (contract->details.v1.choices);
1667 1 : for (unsigned int i = 0;
1668 3 : i < contract->details.v1.token_authorities_len;
1669 2 : i++)
1670 2 : contract_token_family_free (&contract->details.v1.token_authorities[i]);
1671 1 : GNUNET_free (contract->details.v1.token_authorities);
1672 1 : break;
1673 : }
1674 2 : GNUNET_free (contract);
1675 : }
|