Line data Source code
1 : /*
2 : This file is part of TALER
3 : (C) 2024 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 : */
21 : #include "platform.h"
22 : #include <gnunet/gnunet_common.h>
23 : #include <gnunet/gnunet_json_lib.h>
24 : #include <jansson.h>
25 : #include <stdbool.h>
26 : #include <stdint.h>
27 : #include <taler/taler_json_lib.h>
28 : #include <taler/taler_util.h>
29 : #include "taler_merchant_util.h"
30 :
31 :
32 : /**
33 : * Parse merchant details of given JSON contract terms.
34 : *
35 : * @param cls closure, unused parameter
36 : * @param root the JSON object representing data
37 : * @param[out] spec where to write the data
38 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
39 : */
40 : static enum GNUNET_GenericReturnValue
41 139 : parse_merchant_details (void *cls,
42 : json_t *root,
43 : struct GNUNET_JSON_Specification *ospec)
44 : {
45 139 : struct TALER_MERCHANT_Contract *contract = ospec->ptr;
46 : struct GNUNET_JSON_Specification spec[] = {
47 139 : GNUNET_JSON_spec_string_copy ("name",
48 : &contract->merchant.name),
49 139 : GNUNET_JSON_spec_mark_optional (
50 : GNUNET_JSON_spec_string_copy ("email",
51 : &contract->merchant.email),
52 : NULL),
53 139 : GNUNET_JSON_spec_mark_optional (
54 : GNUNET_JSON_spec_string_copy ("website",
55 : &contract->merchant.website),
56 : NULL),
57 139 : GNUNET_JSON_spec_mark_optional (
58 : GNUNET_JSON_spec_string_copy ("logo",
59 : &contract->merchant.logo),
60 : NULL),
61 139 : GNUNET_JSON_spec_mark_optional (
62 : GNUNET_JSON_spec_object_copy ("address",
63 : &contract->merchant.address),
64 : NULL),
65 139 : GNUNET_JSON_spec_mark_optional (
66 : GNUNET_JSON_spec_object_copy ("jurisdiction",
67 : &contract->merchant.jurisdiction),
68 : NULL),
69 139 : GNUNET_JSON_spec_end ()
70 : };
71 : const char *error_name;
72 : unsigned int error_line;
73 :
74 : (void) cls;
75 139 : if (GNUNET_OK !=
76 139 : GNUNET_JSON_parse (root,
77 : spec,
78 : &error_name,
79 : &error_line))
80 : {
81 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
82 : "Failed to parse %s at %u: %s\n",
83 : spec[error_line].field,
84 : error_line,
85 : error_name);
86 0 : GNUNET_break_op (0);
87 0 : return GNUNET_SYSERR;
88 : }
89 139 : return GNUNET_OK;
90 : }
91 :
92 :
93 : /**
94 : * Provide specification to parse given JSON object to merchant details in the
95 : * contract terms. All fields from @a contract are copied.
96 : *
97 : * @param name name of the merchant details field in the JSON
98 : * @param[out] contract where the merchant details have to be written
99 : */
100 : static struct GNUNET_JSON_Specification
101 139 : spec_merchant_details (const char *name,
102 : struct TALER_MERCHANT_Contract *contract)
103 : {
104 139 : struct GNUNET_JSON_Specification ret = {
105 : .parser = &parse_merchant_details,
106 : .field = name,
107 : .ptr = contract,
108 : };
109 :
110 139 : return ret;
111 : }
112 :
113 :
114 : /**
115 : * Get enum value from contract token type string.
116 : *
117 : * @param str contract token type string
118 : * @return enum value of token type
119 : */
120 : static enum TALER_MERCHANT_ContractTokenKind
121 9 : contract_token_kind_from_string (const char *str)
122 : {
123 9 : if (0 == strcmp ("subscription",
124 : str))
125 7 : return TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION;
126 2 : if (0 == strcmp ("discount",
127 : str))
128 2 : return TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT;
129 0 : return TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID;
130 : }
131 :
132 :
133 : enum GNUNET_GenericReturnValue
134 15 : TALER_MERCHANT_parse_choice_input (
135 : json_t *root,
136 : struct TALER_MERCHANT_ContractInput *input,
137 : size_t index,
138 : bool order)
139 : {
140 : const char *ename;
141 : unsigned int eline;
142 : struct GNUNET_JSON_Specification ispec[] = {
143 15 : TALER_MERCHANT_json_spec_cit ("type",
144 : &input->type),
145 15 : GNUNET_JSON_spec_end ()
146 : };
147 :
148 15 : if (GNUNET_OK !=
149 15 : GNUNET_JSON_parse (root,
150 : ispec,
151 : &ename,
152 : &eline))
153 : {
154 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
155 : "Failed to parse %s at %u: %s\n",
156 : ispec[eline].field,
157 : eline,
158 : ename);
159 0 : GNUNET_break_op (0);
160 0 : return GNUNET_SYSERR;
161 : }
162 :
163 15 : switch (input->type)
164 : {
165 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
166 0 : GNUNET_break (0);
167 0 : break;
168 15 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
169 : {
170 : struct GNUNET_JSON_Specification spec[] = {
171 15 : GNUNET_JSON_spec_string ("token_family_slug",
172 : &input->details.token.token_family_slug),
173 15 : GNUNET_JSON_spec_mark_optional (
174 : GNUNET_JSON_spec_uint32 ("count",
175 15 : &input->details.token.count),
176 : NULL),
177 15 : GNUNET_JSON_spec_end ()
178 : };
179 :
180 15 : if (GNUNET_OK !=
181 15 : GNUNET_JSON_parse (root,
182 : spec,
183 : &ename,
184 : &eline))
185 : {
186 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
187 : "Failed to parse %s at %u: %s\n",
188 : spec[eline].field,
189 : eline,
190 : ename);
191 0 : GNUNET_break_op (0);
192 0 : return GNUNET_SYSERR;
193 : }
194 :
195 15 : return GNUNET_OK;
196 : }
197 : }
198 :
199 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
200 : "Field 'type' invalid in input #%u\n",
201 : (unsigned int) index);
202 0 : GNUNET_break_op (0);
203 0 : return GNUNET_SYSERR;
204 : }
205 :
206 :
207 : enum GNUNET_GenericReturnValue
208 16 : TALER_MERCHANT_parse_choice_output (
209 : json_t *root,
210 : struct TALER_MERCHANT_ContractOutput *output,
211 : size_t index,
212 : bool order)
213 : {
214 : const char *ename;
215 : unsigned int eline;
216 : struct GNUNET_JSON_Specification ispec[] = {
217 16 : TALER_MERCHANT_json_spec_cot ("type",
218 : &output->type),
219 16 : GNUNET_JSON_spec_end ()
220 : };
221 :
222 16 : if (GNUNET_OK !=
223 16 : GNUNET_JSON_parse (root,
224 : ispec,
225 : &ename,
226 : &eline))
227 : {
228 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
229 : "Failed to parse %s at %u: %s\n",
230 : ispec[eline].field,
231 : eline,
232 : ename);
233 0 : GNUNET_break_op (0);
234 0 : return GNUNET_SYSERR;
235 : }
236 :
237 16 : switch (output->type)
238 : {
239 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
240 0 : GNUNET_break (0);
241 0 : break;
242 15 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
243 : {
244 : struct GNUNET_JSON_Specification spec[] = {
245 15 : GNUNET_JSON_spec_string ("token_family_slug",
246 : &output->details.token.token_family_slug),
247 15 : GNUNET_JSON_spec_mark_optional (
248 : GNUNET_JSON_spec_uint ("count",
249 : &output->details.token.count),
250 : NULL),
251 15 : GNUNET_JSON_spec_mark_optional (
252 : GNUNET_JSON_spec_timestamp ("valid_at",
253 : &output->details.token.valid_at),
254 : NULL),
255 15 : (! order)
256 7 : ? GNUNET_JSON_spec_uint ("key_index",
257 : &output->details.token.key_index)
258 15 : : GNUNET_JSON_spec_end (),
259 15 : GNUNET_JSON_spec_end ()
260 : };
261 :
262 15 : if (GNUNET_OK !=
263 15 : GNUNET_JSON_parse (root,
264 : spec,
265 : &ename,
266 : &eline))
267 : {
268 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
269 : "Failed to parse %s at %u: %s\n",
270 : spec[eline].field,
271 : eline,
272 : ename);
273 0 : GNUNET_break_op (0);
274 0 : return GNUNET_SYSERR;
275 : }
276 :
277 15 : return GNUNET_OK;
278 : }
279 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
280 : {
281 1 : const json_t *donau_urls = NULL;
282 : struct GNUNET_JSON_Specification spec[] = {
283 1 : GNUNET_JSON_spec_mark_optional (
284 : TALER_JSON_spec_amount_any ("amount",
285 : &output->details.donation_receipt.amount),
286 : NULL),
287 1 : (! order)
288 1 : ? GNUNET_JSON_spec_array_const ("donau_urls",
289 : &donau_urls)
290 1 : : GNUNET_JSON_spec_end (),
291 1 : GNUNET_JSON_spec_end ()
292 : };
293 :
294 1 : if (GNUNET_OK !=
295 1 : GNUNET_JSON_parse (root,
296 : spec,
297 : &ename,
298 : &eline))
299 : {
300 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
301 : "Failed to parse %s at %u: %s\n",
302 : spec[eline].field,
303 : eline,
304 : ename);
305 0 : GNUNET_break_op (0);
306 0 : return GNUNET_SYSERR;
307 : }
308 :
309 1 : GNUNET_array_grow (output->details.donation_receipt.donau_urls,
310 : output->details.donation_receipt.donau_urls_len,
311 : json_array_size (donau_urls));
312 :
313 1 : for (unsigned int i = 0;
314 4 : i < output->details.donation_receipt.donau_urls_len;
315 3 : i++)
316 : {
317 : const json_t *jurl;
318 :
319 3 : jurl = json_array_get (donau_urls,
320 : i);
321 3 : if (! json_is_string (jurl))
322 : {
323 0 : GNUNET_break_op (0);
324 0 : return GNUNET_SYSERR;
325 : }
326 :
327 3 : output->details.donation_receipt.donau_urls[i] =
328 3 : 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 :
350 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
351 : */
352 : static enum GNUNET_GenericReturnValue
353 8 : parse_choices (
354 : void *cls,
355 : json_t *root,
356 : struct GNUNET_JSON_Specification *ospec)
357 : {
358 8 : struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr;
359 8 : unsigned int *choices_len = cls;
360 :
361 8 : if (! json_is_array (root))
362 : {
363 0 : GNUNET_break_op (0);
364 0 : return GNUNET_SYSERR;
365 : }
366 :
367 8 : GNUNET_array_grow (*choices,
368 : *choices_len,
369 : json_array_size (root));
370 :
371 17 : for (unsigned int i = 0; i < *choices_len; i++)
372 : {
373 9 : struct TALER_MERCHANT_ContractChoice *choice = &(*choices)[i];
374 : const json_t *jinputs;
375 : const json_t *joutputs;
376 :
377 : struct GNUNET_JSON_Specification spec[] = {
378 9 : TALER_JSON_spec_amount_any ("amount",
379 : &choice->amount),
380 9 : TALER_JSON_spec_amount_any ("max_fee",
381 : &choice->max_fee),
382 9 : GNUNET_JSON_spec_array_const ("inputs",
383 : &jinputs),
384 9 : GNUNET_JSON_spec_array_const ("outputs",
385 : &joutputs),
386 9 : GNUNET_JSON_spec_end ()
387 : };
388 :
389 : const char *ename;
390 : unsigned int eline;
391 :
392 9 : if (GNUNET_OK !=
393 9 : GNUNET_JSON_parse (json_array_get (root, i),
394 : spec,
395 : &ename,
396 : &eline))
397 : {
398 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
399 : "Failed to parse %s at %u: %s\n",
400 : spec[eline].field,
401 : eline,
402 : ename);
403 0 : GNUNET_break_op (0);
404 0 : return GNUNET_SYSERR;
405 : }
406 :
407 : {
408 : const json_t *jinput;
409 : size_t idx;
410 15 : json_array_foreach ((json_t *) jinputs, idx, jinput)
411 : {
412 6 : struct TALER_MERCHANT_ContractInput input = {
413 : .details.token.count = 1
414 : };
415 :
416 6 : if (GNUNET_OK !=
417 6 : TALER_MERCHANT_parse_choice_input ((json_t *) jinput,
418 : &input,
419 : idx,
420 : false))
421 0 : return GNUNET_SYSERR;
422 :
423 6 : switch (input.type)
424 : {
425 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
426 0 : GNUNET_break_op (0);
427 0 : return GNUNET_SYSERR;
428 6 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
429 : /* Ignore inputs tokens with 'count' field set to 0 */
430 6 : if (0 == input.details.token.count)
431 0 : continue;
432 6 : break;
433 : }
434 :
435 6 : GNUNET_array_append (choice->inputs,
436 : choice->inputs_len,
437 : input);
438 : }
439 : }
440 :
441 : {
442 : const json_t *joutput;
443 : size_t idx;
444 17 : json_array_foreach ((json_t *) joutputs, idx, joutput)
445 : {
446 8 : struct TALER_MERCHANT_ContractOutput output = {
447 : .details.token.count = 1
448 : };
449 :
450 8 : if (GNUNET_OK !=
451 8 : TALER_MERCHANT_parse_choice_output ((json_t *) joutput,
452 : &output,
453 : idx,
454 : false))
455 0 : return GNUNET_SYSERR;
456 :
457 8 : switch (output.type)
458 : {
459 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
460 0 : GNUNET_break_op (0);
461 0 : return GNUNET_SYSERR;
462 7 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
463 : /* Ignore output tokens with 'count' field set to 0 */
464 7 : if (0 == output.details.token.count)
465 0 : continue;
466 7 : break;
467 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
468 1 : break;
469 : }
470 :
471 8 : GNUNET_array_append (choice->outputs,
472 : choice->outputs_len,
473 : output);
474 : }
475 : }
476 : }
477 :
478 8 : return GNUNET_OK;
479 : }
480 :
481 :
482 : /**
483 : * Provide specification to parse given JSON array to contract terms
484 : * choices. All fields from @a choices elements are copied.
485 : *
486 : * @param name name of the choices field in the JSON
487 : * @param[out] choices where the contract choices array has to be written
488 : * @param[out] choices_len length of the @a choices array
489 : */
490 : static struct GNUNET_JSON_Specification
491 8 : spec_choices (
492 : const char *name,
493 : struct TALER_MERCHANT_ContractChoice **choices,
494 : unsigned int *choices_len)
495 : {
496 8 : struct GNUNET_JSON_Specification ret = {
497 : .cls = (void *) choices_len,
498 : .parser = &parse_choices,
499 : .field = name,
500 : .ptr = choices,
501 : };
502 :
503 8 : return ret;
504 : }
505 :
506 :
507 : /**
508 : * Free all the fields in the given @a choice, but not @a choice itself, since
509 : * it is normally part of an array.
510 : *
511 : * @param[in] choice contract terms choice to free
512 : */
513 : static void
514 9 : contract_choice_free (
515 : struct TALER_MERCHANT_ContractChoice *choice)
516 : {
517 17 : for (unsigned int i = 0; i < choice->outputs_len; i++)
518 : {
519 8 : struct TALER_MERCHANT_ContractOutput *output = &choice->outputs[i];
520 :
521 8 : GNUNET_free (output->details.coin.exchange_url);
522 : }
523 9 : GNUNET_free (choice->inputs);
524 9 : GNUNET_free (choice->outputs);
525 9 : }
526 :
527 :
528 : /**
529 : * Parse token details of the given JSON contract token family.
530 : *
531 : * @param cls closure, unused parameter
532 : * @param root the JSON object representing data
533 : * @param[out] ospec where to write the data
534 : * @return #GNUNET_OK upon successful parsing; @GNUNET_SYSERR upon error
535 : */
536 : static enum GNUNET_GenericReturnValue
537 9 : parse_token_details (void *cls,
538 : json_t *root,
539 : struct GNUNET_JSON_Specification *ospec)
540 : {
541 9 : struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
542 : const char *class;
543 : const char *ename;
544 : unsigned int eline;
545 : struct GNUNET_JSON_Specification ispec[] = {
546 9 : GNUNET_JSON_spec_string ("class",
547 : &class),
548 9 : GNUNET_JSON_spec_end ()
549 : };
550 :
551 : (void) cls;
552 9 : if (GNUNET_OK !=
553 9 : GNUNET_JSON_parse (root,
554 : ispec,
555 : &ename,
556 : &eline))
557 : {
558 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
559 : "Failed to parse %s at %u: %s\n",
560 : ispec[eline].field,
561 : eline,
562 : ename);
563 0 : GNUNET_break_op (0);
564 0 : return GNUNET_SYSERR;
565 : }
566 :
567 9 : family->kind = contract_token_kind_from_string (class);
568 :
569 9 : switch (family->kind)
570 : {
571 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
572 0 : break;
573 7 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
574 : {
575 : const json_t *trusted_domains;
576 : struct GNUNET_JSON_Specification spec[] = {
577 7 : GNUNET_JSON_spec_array_const ("trusted_domains",
578 : &trusted_domains),
579 7 : GNUNET_JSON_spec_end ()
580 : };
581 :
582 7 : if (GNUNET_OK !=
583 7 : GNUNET_JSON_parse (root,
584 : spec,
585 : &ename,
586 : &eline))
587 : {
588 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
589 : "Failed to parse %s at %u: %s\n",
590 : spec[eline].field,
591 : eline,
592 : ename);
593 0 : GNUNET_break_op (0);
594 0 : return GNUNET_SYSERR;
595 : }
596 :
597 7 : GNUNET_array_grow (family->details.subscription.trusted_domains,
598 : family->details.subscription.trusted_domains_len,
599 : json_array_size (trusted_domains));
600 :
601 7 : for (unsigned int i = 0;
602 10 : i < family->details.subscription.trusted_domains_len;
603 3 : i++)
604 : {
605 : const json_t *jdomain;
606 3 : jdomain = json_array_get (trusted_domains, i);
607 :
608 3 : if (! json_is_string (jdomain))
609 : {
610 0 : GNUNET_break_op (0);
611 0 : return GNUNET_SYSERR;
612 : }
613 :
614 3 : family->details.subscription.trusted_domains[i] =
615 3 : GNUNET_strdup (json_string_value (jdomain));
616 : }
617 :
618 7 : return GNUNET_OK;
619 : }
620 2 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
621 : {
622 : const json_t *expected_domains;
623 : struct GNUNET_JSON_Specification spec[] = {
624 2 : GNUNET_JSON_spec_array_const ("expected_domains",
625 : &expected_domains),
626 2 : GNUNET_JSON_spec_end ()
627 : };
628 :
629 2 : if (GNUNET_OK !=
630 2 : GNUNET_JSON_parse (root,
631 : spec,
632 : &ename,
633 : &eline))
634 : {
635 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
636 : "Failed to parse %s at %u: %s\n",
637 : spec[eline].field,
638 : eline,
639 : ename);
640 0 : GNUNET_break_op (0);
641 0 : return GNUNET_SYSERR;
642 : }
643 :
644 2 : GNUNET_array_grow (family->details.discount.expected_domains,
645 : family->details.discount.expected_domains_len,
646 : json_array_size (expected_domains));
647 :
648 2 : for (unsigned int i = 0;
649 5 : i < family->details.discount.expected_domains_len;
650 3 : i++)
651 : {
652 : const json_t *jdomain;
653 :
654 3 : jdomain = json_array_get (expected_domains,
655 : i);
656 3 : if (! json_is_string (jdomain))
657 : {
658 0 : GNUNET_break_op (0);
659 0 : return GNUNET_SYSERR;
660 : }
661 :
662 3 : family->details.discount.expected_domains[i] =
663 3 : GNUNET_strdup (json_string_value (jdomain));
664 : }
665 :
666 2 : return GNUNET_OK;
667 : }
668 : }
669 :
670 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
671 : "Field 'type' invalid in token family\n");
672 0 : GNUNET_break_op (0);
673 0 : return GNUNET_SYSERR;
674 : }
675 :
676 :
677 : /**
678 : * Provide specification to parse given JSON object to contract token details
679 : * in the contract token family. All fields from @a family are copied.
680 : *
681 : * @param name name of the token details field in the JSON
682 : * @param[out] token family where the token details have to be written
683 : */
684 : static struct GNUNET_JSON_Specification
685 9 : spec_token_details (const char *name,
686 : struct TALER_MERCHANT_ContractTokenFamily *family)
687 : {
688 9 : struct GNUNET_JSON_Specification ret = {
689 : .parser = &parse_token_details,
690 : .field = name,
691 : .ptr = family,
692 : };
693 :
694 9 : return ret;
695 : }
696 :
697 :
698 : /**
699 : * Parse given JSON object to token families array.
700 : *
701 : * @param cls closure, pointer to array length
702 : * @param root the json object representing the token families. The keys are
703 : * the token family slugs.
704 : * @param[out] ospec where to write the data
705 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
706 : */
707 : static enum GNUNET_GenericReturnValue
708 8 : parse_token_families (void *cls,
709 : json_t *root,
710 : struct GNUNET_JSON_Specification *ospec)
711 : {
712 8 : struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
713 8 : unsigned int *families_len = cls;
714 : json_t *jfamily;
715 : const char *slug;
716 :
717 8 : if (! json_is_object (root))
718 : {
719 0 : GNUNET_break_op (0);
720 0 : return GNUNET_SYSERR;
721 : }
722 :
723 17 : json_object_foreach (root, slug, jfamily)
724 : {
725 : const json_t *keys;
726 18 : struct TALER_MERCHANT_ContractTokenFamily family = {
727 9 : .slug = GNUNET_strdup (slug)
728 : };
729 : struct GNUNET_JSON_Specification spec[] = {
730 9 : GNUNET_JSON_spec_string_copy ("name",
731 : &family.name),
732 9 : GNUNET_JSON_spec_string_copy ("description",
733 : &family.description),
734 9 : GNUNET_JSON_spec_object_copy ("description_i18n",
735 : &family.description_i18n),
736 9 : GNUNET_JSON_spec_array_const ("keys",
737 : &keys),
738 9 : spec_token_details ("details",
739 : &family),
740 9 : GNUNET_JSON_spec_bool ("critical",
741 : &family.critical),
742 9 : GNUNET_JSON_spec_end ()
743 : };
744 : const char *error_name;
745 : unsigned int error_line;
746 :
747 9 : if (GNUNET_OK !=
748 9 : GNUNET_JSON_parse (jfamily,
749 : spec,
750 : &error_name,
751 : &error_line))
752 : {
753 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
754 : "Failed to parse %s at %u: %s\n",
755 : spec[error_line].field,
756 : error_line,
757 : error_name);
758 0 : GNUNET_break_op (0);
759 0 : return GNUNET_SYSERR;
760 : }
761 :
762 9 : GNUNET_array_grow (family.keys,
763 : family.keys_len,
764 : json_array_size (keys));
765 :
766 17 : for (unsigned int i = 0; i<family.keys_len; i++)
767 : {
768 8 : struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
769 : struct GNUNET_JSON_Specification key_spec[] = {
770 8 : TALER_JSON_spec_token_pub (
771 : NULL,
772 : &key->pub),
773 8 : GNUNET_JSON_spec_timestamp (
774 : "signature_validity_start",
775 : &key->valid_after),
776 8 : GNUNET_JSON_spec_timestamp (
777 : "signature_validity_end",
778 : &key->valid_before),
779 8 : GNUNET_JSON_spec_end ()
780 : };
781 : const char *ierror_name;
782 : unsigned int ierror_line;
783 :
784 8 : if (GNUNET_OK !=
785 8 : GNUNET_JSON_parse (json_array_get (keys,
786 : i),
787 : key_spec,
788 : &ierror_name,
789 : &ierror_line))
790 : {
791 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
792 : "Failed to parse %s at %u: %s\n",
793 : key_spec[ierror_line].field,
794 : ierror_line,
795 : ierror_name);
796 0 : GNUNET_break_op (0);
797 0 : return GNUNET_SYSERR;
798 : }
799 : }
800 :
801 9 : GNUNET_array_append (*families,
802 : *families_len,
803 : family);
804 : }
805 :
806 8 : return GNUNET_OK;
807 : }
808 :
809 :
810 : /**
811 : * Provide specification to parse given JSON array to token families in the
812 : * contract terms. All fields from @a families items are copied.
813 : *
814 : * @param name name of the token families field in the JSON
815 : * @param[out] families where the token families array has to be written
816 : * @param[out] families_len length of the @a families array
817 : */
818 : static struct GNUNET_JSON_Specification
819 8 : spec_token_families (
820 : const char *name,
821 : struct TALER_MERCHANT_ContractTokenFamily **families,
822 : unsigned int *families_len)
823 : {
824 8 : struct GNUNET_JSON_Specification ret = {
825 : .cls = (void *) families_len,
826 : .parser = &parse_token_families,
827 : .field = name,
828 : .ptr = families,
829 : };
830 :
831 8 : return ret;
832 : }
833 :
834 :
835 : enum GNUNET_GenericReturnValue
836 2 : TALER_MERCHANT_find_token_family_key (
837 : const char *slug,
838 : struct GNUNET_TIME_Timestamp valid_after,
839 : const struct TALER_MERCHANT_ContractTokenFamily *families,
840 : unsigned int families_len,
841 : struct TALER_MERCHANT_ContractTokenFamily *family,
842 : struct TALER_MERCHANT_ContractTokenFamilyKey *key)
843 : {
844 3 : for (unsigned int i = 0; i < families_len; i++)
845 : {
846 3 : const struct TALER_MERCHANT_ContractTokenFamily *fami
847 3 : = &families[i];
848 :
849 3 : if (0 != strcmp (fami->slug,
850 : slug))
851 1 : continue;
852 2 : if (NULL != family)
853 2 : *family = *fami;
854 2 : for (unsigned int k = 0; k < fami->keys_len; k++)
855 : {
856 2 : struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
857 :
858 2 : if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
859 : ==,
860 : valid_after))
861 : {
862 2 : if (NULL != key)
863 2 : *key = *ki;
864 2 : return GNUNET_OK;
865 : }
866 : }
867 : /* matching family found, but no key. */
868 0 : return GNUNET_NO;
869 : }
870 :
871 : /* no matching family found */
872 0 : return GNUNET_SYSERR;
873 : }
874 :
875 :
876 : /**
877 : * Free all the fields in the given @a family, but not @a family itself, since
878 : * it is normally part of an array.
879 : *
880 : * @param[in] family contract token family to free
881 : */
882 : static void
883 9 : contract_token_family_free (
884 : struct TALER_MERCHANT_ContractTokenFamily *family)
885 : {
886 9 : GNUNET_free (family->slug);
887 9 : GNUNET_free (family->name);
888 9 : GNUNET_free (family->description);
889 9 : if (NULL != family->description_i18n)
890 : {
891 9 : json_decref (family->description_i18n);
892 9 : family->description_i18n = NULL;
893 : }
894 17 : for (unsigned int i = 0; i < family->keys_len; i++)
895 8 : TALER_token_issue_pub_free (&family->keys[i].pub);
896 9 : GNUNET_free (family->keys);
897 :
898 9 : switch (family->kind)
899 : {
900 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
901 0 : break;
902 2 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
903 5 : for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
904 3 : i++)
905 3 : GNUNET_free (family->details.discount.expected_domains[i]);
906 2 : break;
907 7 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
908 10 : for (unsigned int i = 0; i < family->details.subscription.
909 3 : trusted_domains_len; i++)
910 3 : GNUNET_free (family->details.subscription.trusted_domains[i]);
911 7 : break;
912 : }
913 9 : }
914 :
915 :
916 : /**
917 : * Parse contract version of given JSON contract terms.
918 : *
919 : * @param cls closure, unused parameter
920 : * @param root the JSON object representing data
921 : * @param[out] spec where to write the data
922 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
923 : */
924 : static enum GNUNET_GenericReturnValue
925 139 : parse_contract_version (void *cls,
926 : json_t *root,
927 : struct GNUNET_JSON_Specification *spec)
928 : {
929 139 : enum TALER_MERCHANT_ContractVersion *res
930 : = (enum TALER_MERCHANT_ContractVersion *) spec->ptr;
931 :
932 : (void) cls;
933 139 : if (json_is_integer (root))
934 : {
935 139 : json_int_t version = json_integer_value (root);
936 :
937 139 : switch (version)
938 : {
939 131 : case 0:
940 131 : *res = TALER_MERCHANT_CONTRACT_VERSION_0;
941 131 : return GNUNET_OK;
942 8 : case 1:
943 8 : *res = TALER_MERCHANT_CONTRACT_VERSION_1;
944 8 : return GNUNET_OK;
945 : }
946 :
947 0 : GNUNET_break_op (0);
948 0 : return GNUNET_SYSERR;
949 : }
950 :
951 0 : if (json_is_null (root))
952 : {
953 0 : *res = TALER_MERCHANT_CONTRACT_VERSION_0;
954 0 : return GNUNET_OK;
955 : }
956 :
957 0 : GNUNET_break_op (0);
958 0 : return GNUNET_SYSERR;
959 : }
960 :
961 :
962 : /**
963 : * Create JSON specification to parse a merchant contract
964 : * version.
965 : *
966 : * @param name name of the field
967 : * @param[out] version where to write the contract version
968 : * @return JSON specification object
969 : */
970 : static struct GNUNET_JSON_Specification
971 139 : spec_contract_version (
972 : const char *name,
973 : enum TALER_MERCHANT_ContractVersion *version)
974 : {
975 139 : struct GNUNET_JSON_Specification ret = {
976 : .parser = &parse_contract_version,
977 : .field = name,
978 : .ptr = version
979 : };
980 :
981 139 : *version = TALER_MERCHANT_CONTRACT_VERSION_0;
982 139 : return ret;
983 : }
984 :
985 :
986 : /**
987 : * Parse v0-specific fields of @a input JSON into @a contract.
988 : *
989 : * @param[in] input the JSON contract terms
990 : * @param[out] contract where to write the data
991 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
992 : */
993 : static enum GNUNET_GenericReturnValue
994 131 : parse_contract_v0 (
995 : json_t *input,
996 : struct TALER_MERCHANT_Contract *contract)
997 : {
998 : struct GNUNET_JSON_Specification espec[] = {
999 131 : TALER_JSON_spec_amount_any ("amount",
1000 : &contract->details.v0.brutto),
1001 131 : TALER_JSON_spec_amount_any ("max_fee",
1002 : &contract->details.v0.max_fee),
1003 131 : GNUNET_JSON_spec_end ()
1004 : };
1005 : enum GNUNET_GenericReturnValue res;
1006 : const char *ename;
1007 : unsigned int eline;
1008 :
1009 131 : res = GNUNET_JSON_parse (input,
1010 : espec,
1011 : &ename,
1012 : &eline);
1013 131 : if (GNUNET_OK != res)
1014 : {
1015 0 : GNUNET_break (0);
1016 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1017 : "Failed to parse contract v0 at field %s\n",
1018 : ename);
1019 0 : return GNUNET_SYSERR;
1020 : }
1021 :
1022 131 : if (GNUNET_OK !=
1023 131 : TALER_amount_cmp_currency (&contract->details.v0.max_fee,
1024 131 : &contract->details.v0.brutto))
1025 : {
1026 0 : GNUNET_break (0);
1027 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1028 : "'max_fee' in database does not match currency of contract price");
1029 0 : return GNUNET_SYSERR;
1030 : }
1031 :
1032 131 : return res;
1033 : }
1034 :
1035 :
1036 : /**
1037 : * Parse v1-specific fields of @a input JSON into @a contract.
1038 : *
1039 : * @param[in] input the JSON contract terms
1040 : * @param[out] contract where to write the data
1041 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1042 : */
1043 : static enum GNUNET_GenericReturnValue
1044 8 : parse_contract_v1 (
1045 : json_t *input,
1046 : struct TALER_MERCHANT_Contract *contract)
1047 : {
1048 : struct GNUNET_JSON_Specification espec[] = {
1049 8 : spec_choices ("choices",
1050 : &contract->details.v1.choices,
1051 : &contract->details.v1.choices_len),
1052 8 : spec_token_families ("token_families",
1053 : &contract->details.v1.token_authorities,
1054 : &contract->details.v1.
1055 : token_authorities_len),
1056 8 : GNUNET_JSON_spec_end ()
1057 : };
1058 :
1059 : enum GNUNET_GenericReturnValue res;
1060 : const char *ename;
1061 : unsigned int eline;
1062 :
1063 8 : res = GNUNET_JSON_parse (input,
1064 : espec,
1065 : &ename,
1066 : &eline);
1067 :
1068 8 : if (GNUNET_OK != res)
1069 : {
1070 0 : GNUNET_break (0);
1071 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1072 : "Failed to parse contract v1 at field %s\n",
1073 : ename);
1074 0 : return GNUNET_SYSERR;
1075 : }
1076 :
1077 8 : return res;
1078 : }
1079 :
1080 :
1081 : struct TALER_MERCHANT_Contract *
1082 139 : TALER_MERCHANT_contract_parse (json_t *input,
1083 : bool nonce_optional)
1084 : {
1085 : struct TALER_MERCHANT_Contract *contract
1086 139 : = GNUNET_new (struct TALER_MERCHANT_Contract);
1087 : struct GNUNET_JSON_Specification espec[] = {
1088 139 : spec_contract_version ("version",
1089 : &contract->version),
1090 139 : GNUNET_JSON_spec_string_copy ("summary",
1091 : &contract->summary),
1092 : /* FIXME: do i18n_str validation in the future */
1093 139 : GNUNET_JSON_spec_mark_optional (
1094 : GNUNET_JSON_spec_object_copy ("summary_i18n",
1095 : &contract->summary_i18n),
1096 : NULL),
1097 139 : GNUNET_JSON_spec_string_copy ("order_id",
1098 : &contract->order_id),
1099 139 : GNUNET_JSON_spec_mark_optional (
1100 : GNUNET_JSON_spec_string_copy ("public_reorder_url",
1101 : &contract->public_reorder_url),
1102 : NULL),
1103 139 : GNUNET_JSON_spec_mark_optional (
1104 : GNUNET_JSON_spec_string_copy ("fulfillment_url",
1105 : &contract->fulfillment_url),
1106 : NULL),
1107 139 : GNUNET_JSON_spec_mark_optional (
1108 : GNUNET_JSON_spec_string_copy ("fulfillment_message",
1109 : &contract->fulfillment_message),
1110 : NULL),
1111 139 : GNUNET_JSON_spec_mark_optional (
1112 : GNUNET_JSON_spec_object_copy ("fulfillment_message_i18n",
1113 : &contract->fulfillment_message_i18n),
1114 : NULL),
1115 139 : GNUNET_JSON_spec_array_copy ("products",
1116 : &contract->products),
1117 139 : GNUNET_JSON_spec_timestamp ("timestamp",
1118 : &contract->timestamp),
1119 139 : GNUNET_JSON_spec_timestamp ("refund_deadline",
1120 : &contract->refund_deadline),
1121 139 : GNUNET_JSON_spec_timestamp ("pay_deadline",
1122 : &contract->pay_deadline),
1123 139 : GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
1124 : &contract->wire_deadline),
1125 139 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
1126 : &contract->merchant_pub),
1127 139 : GNUNET_JSON_spec_string_copy ("merchant_base_url",
1128 : &contract->merchant_base_url),
1129 139 : spec_merchant_details ("merchant",
1130 : contract),
1131 139 : GNUNET_JSON_spec_fixed_auto ("h_wire",
1132 : &contract->h_wire),
1133 139 : GNUNET_JSON_spec_string_copy ("wire_method",
1134 : &contract->wire_method),
1135 139 : GNUNET_JSON_spec_array_copy ("exchanges",
1136 : &contract->exchanges),
1137 139 : GNUNET_JSON_spec_mark_optional (
1138 : GNUNET_JSON_spec_object_copy ("delivery_location",
1139 : &contract->delivery_location),
1140 : NULL),
1141 139 : GNUNET_JSON_spec_mark_optional (
1142 : GNUNET_JSON_spec_timestamp ("delivery_date",
1143 : &contract->delivery_date),
1144 : NULL),
1145 : (nonce_optional)
1146 137 : ? GNUNET_JSON_spec_mark_optional (
1147 : GNUNET_JSON_spec_string_copy ("nonce",
1148 : &contract->nonce),
1149 : NULL)
1150 139 : : GNUNET_JSON_spec_string_copy ("nonce",
1151 : &contract->nonce),
1152 139 : GNUNET_JSON_spec_mark_optional (
1153 : GNUNET_JSON_spec_relative_time ("auto_refund",
1154 : &contract->auto_refund),
1155 : NULL),
1156 139 : GNUNET_JSON_spec_mark_optional (
1157 : GNUNET_JSON_spec_object_copy ("extra",
1158 : &contract->extra),
1159 : NULL),
1160 139 : GNUNET_JSON_spec_mark_optional (
1161 : GNUNET_JSON_spec_uint8 ("minimum_age",
1162 : &contract->minimum_age),
1163 : NULL),
1164 139 : GNUNET_JSON_spec_end ()
1165 : };
1166 :
1167 : enum GNUNET_GenericReturnValue res;
1168 : const char *ename;
1169 : unsigned int eline;
1170 :
1171 139 : GNUNET_assert (NULL != input);
1172 139 : res = GNUNET_JSON_parse (input,
1173 : espec,
1174 : &ename,
1175 : &eline);
1176 139 : if (GNUNET_OK != res)
1177 : {
1178 0 : GNUNET_break (0);
1179 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1180 : "Failed to parse contract at field %s\n",
1181 : ename);
1182 0 : goto cleanup;
1183 : }
1184 :
1185 139 : switch (contract->version)
1186 : {
1187 131 : case TALER_MERCHANT_CONTRACT_VERSION_0:
1188 131 : res = parse_contract_v0 (input,
1189 : contract);
1190 131 : break;
1191 8 : case TALER_MERCHANT_CONTRACT_VERSION_1:
1192 8 : res = parse_contract_v1 (input,
1193 : contract);
1194 8 : break;
1195 : }
1196 :
1197 139 : if (GNUNET_OK != res)
1198 : {
1199 0 : GNUNET_break (0);
1200 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1201 : "Failed to parse contract\n");
1202 0 : goto cleanup;
1203 : }
1204 139 : return contract;
1205 :
1206 0 : cleanup:
1207 0 : TALER_MERCHANT_contract_free (contract);
1208 0 : return NULL;
1209 : }
1210 :
1211 :
1212 : void
1213 139 : TALER_MERCHANT_contract_free (
1214 : struct TALER_MERCHANT_Contract *contract)
1215 : {
1216 139 : if (NULL == contract)
1217 0 : return;
1218 139 : GNUNET_free (contract->public_reorder_url);
1219 139 : GNUNET_free (contract->order_id);
1220 139 : GNUNET_free (contract->merchant_base_url);
1221 139 : GNUNET_free (contract->merchant.name);
1222 139 : GNUNET_free (contract->merchant.website);
1223 139 : GNUNET_free (contract->merchant.email);
1224 139 : GNUNET_free (contract->merchant.logo);
1225 139 : if (NULL != contract->merchant.address)
1226 : {
1227 139 : json_decref (contract->merchant.address);
1228 139 : contract->merchant.address = NULL;
1229 : }
1230 139 : if (NULL != contract->merchant.jurisdiction)
1231 : {
1232 139 : json_decref (contract->merchant.jurisdiction);
1233 139 : contract->merchant.jurisdiction = NULL;
1234 : }
1235 139 : GNUNET_free (contract->summary);
1236 139 : GNUNET_free (contract->fulfillment_url);
1237 139 : GNUNET_free (contract->fulfillment_message);
1238 139 : if (NULL != contract->fulfillment_message_i18n)
1239 : {
1240 2 : json_decref (contract->fulfillment_message_i18n);
1241 2 : contract->fulfillment_message_i18n = NULL;
1242 : }
1243 139 : if (NULL != contract->products)
1244 : {
1245 139 : json_decref (contract->products);
1246 139 : contract->products = NULL;
1247 : }
1248 139 : GNUNET_free (contract->wire_method);
1249 139 : if (NULL != contract->exchanges)
1250 : {
1251 139 : json_decref (contract->exchanges);
1252 139 : contract->exchanges = NULL;
1253 : }
1254 139 : if (NULL != contract->delivery_location)
1255 : {
1256 2 : json_decref (contract->delivery_location);
1257 2 : contract->delivery_location = NULL;
1258 : }
1259 139 : GNUNET_free (contract->nonce);
1260 139 : if (NULL != contract->extra)
1261 : {
1262 2 : json_decref (contract->extra);
1263 2 : contract->extra = NULL;
1264 : }
1265 :
1266 139 : switch (contract->version)
1267 : {
1268 131 : case TALER_MERCHANT_CONTRACT_VERSION_0:
1269 131 : break;
1270 8 : case TALER_MERCHANT_CONTRACT_VERSION_1:
1271 8 : for (unsigned int i = 0;
1272 17 : i < contract->details.v1.choices_len;
1273 9 : i++)
1274 9 : contract_choice_free (&contract->details.v1.choices[i]);
1275 8 : GNUNET_free (contract->details.v1.choices);
1276 8 : for (unsigned int i = 0;
1277 17 : i < contract->details.v1.token_authorities_len;
1278 9 : i++)
1279 9 : contract_token_family_free (&contract->details.v1.token_authorities[i]);
1280 8 : GNUNET_free (contract->details.v1.token_authorities);
1281 8 : break;
1282 : }
1283 139 : GNUNET_free (contract);
1284 : }
|