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