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] ospec where to write the data
38 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
39 : */
40 : static enum GNUNET_GenericReturnValue
41 161 : parse_merchant_details (void *cls,
42 : json_t *root,
43 : struct GNUNET_JSON_Specification *ospec)
44 : {
45 161 : struct TALER_MERCHANT_Contract *contract = ospec->ptr;
46 : struct GNUNET_JSON_Specification spec[] = {
47 161 : GNUNET_JSON_spec_string_copy ("name",
48 : &contract->merchant.name),
49 161 : GNUNET_JSON_spec_mark_optional (
50 : GNUNET_JSON_spec_string_copy ("email",
51 : &contract->merchant.email),
52 : NULL),
53 161 : GNUNET_JSON_spec_mark_optional (
54 : GNUNET_JSON_spec_string_copy ("website",
55 : &contract->merchant.website),
56 : NULL),
57 161 : GNUNET_JSON_spec_mark_optional (
58 : GNUNET_JSON_spec_string_copy ("logo",
59 : &contract->merchant.logo),
60 : NULL),
61 161 : GNUNET_JSON_spec_mark_optional (
62 : GNUNET_JSON_spec_object_copy ("address",
63 : &contract->merchant.address),
64 : NULL),
65 161 : GNUNET_JSON_spec_mark_optional (
66 : GNUNET_JSON_spec_object_copy ("jurisdiction",
67 : &contract->merchant.jurisdiction),
68 : NULL),
69 161 : GNUNET_JSON_spec_end ()
70 : };
71 : const char *error_name;
72 : unsigned int error_line;
73 :
74 : (void) cls;
75 161 : if (GNUNET_OK !=
76 161 : 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 161 : 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 161 : spec_merchant_details (const char *name,
102 : struct TALER_MERCHANT_Contract *contract)
103 : {
104 161 : struct GNUNET_JSON_Specification ret = {
105 : .parser = &parse_merchant_details,
106 : .field = name,
107 : .ptr = contract,
108 : };
109 :
110 161 : 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 3 : output->details.donation_receipt.donau_urls[i] =
327 3 : GNUNET_strdup (json_string_value (jurl));
328 : }
329 :
330 1 : return GNUNET_OK;
331 : }
332 : }
333 :
334 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
335 : "Field 'type' invalid in output #%u\n",
336 : (unsigned int) index);
337 0 : GNUNET_break_op (0);
338 0 : return GNUNET_SYSERR;
339 : }
340 :
341 :
342 : /**
343 : * Parse given JSON object to choices array.
344 : *
345 : * @param cls closure, pointer to array length
346 : * @param root the json array representing the choices
347 : * @param[out] ospec where to write the data
348 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
349 : */
350 : static enum GNUNET_GenericReturnValue
351 8 : parse_choices (
352 : void *cls,
353 : json_t *root,
354 : struct GNUNET_JSON_Specification *ospec)
355 : {
356 8 : struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr;
357 8 : unsigned int *choices_len = cls;
358 :
359 8 : if (! json_is_array (root))
360 : {
361 0 : GNUNET_break_op (0);
362 0 : return GNUNET_SYSERR;
363 : }
364 :
365 8 : GNUNET_array_grow (*choices,
366 : *choices_len,
367 : json_array_size (root));
368 :
369 17 : for (unsigned int i = 0; i < *choices_len; i++)
370 : {
371 9 : struct TALER_MERCHANT_ContractChoice *choice = &(*choices)[i];
372 : const json_t *jinputs;
373 : const json_t *joutputs;
374 : struct GNUNET_JSON_Specification spec[] = {
375 9 : TALER_JSON_spec_amount_any ("amount",
376 : &choice->amount),
377 9 : GNUNET_JSON_spec_mark_optional (
378 : GNUNET_JSON_spec_string_copy ("description",
379 : &choice->description),
380 : NULL),
381 9 : GNUNET_JSON_spec_mark_optional (
382 : GNUNET_JSON_spec_object_copy ("description_i18n",
383 : &choice->description_i18n),
384 : NULL),
385 9 : TALER_JSON_spec_amount_any ("max_fee",
386 : &choice->max_fee),
387 9 : GNUNET_JSON_spec_array_const ("inputs",
388 : &jinputs),
389 9 : GNUNET_JSON_spec_array_const ("outputs",
390 : &joutputs),
391 9 : GNUNET_JSON_spec_end ()
392 : };
393 : const char *ename;
394 : unsigned int eline;
395 :
396 9 : if (GNUNET_OK !=
397 9 : GNUNET_JSON_parse (json_array_get (root, i),
398 : spec,
399 : &ename,
400 : &eline))
401 : {
402 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
403 : "Failed to parse %s at %u: %s\n",
404 : spec[eline].field,
405 : eline,
406 : ename);
407 0 : GNUNET_break_op (0);
408 0 : return GNUNET_SYSERR;
409 : }
410 :
411 : {
412 : const json_t *jinput;
413 : size_t idx;
414 :
415 15 : json_array_foreach ((json_t *) jinputs, idx, jinput)
416 : {
417 6 : struct TALER_MERCHANT_ContractInput input = {
418 : .details.token.count = 1
419 : };
420 :
421 6 : if (GNUNET_OK !=
422 6 : TALER_MERCHANT_parse_choice_input ((json_t *) jinput,
423 : &input,
424 : idx,
425 : false))
426 : {
427 0 : GNUNET_break (0);
428 0 : return GNUNET_SYSERR;
429 : }
430 6 : switch (input.type)
431 : {
432 0 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID:
433 0 : GNUNET_break_op (0);
434 0 : return GNUNET_SYSERR;
435 6 : case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN:
436 : /* Ignore inputs tokens with 'count' field set to 0 */
437 6 : if (0 == input.details.token.count)
438 0 : continue;
439 6 : break;
440 : }
441 6 : GNUNET_array_append (choice->inputs,
442 : choice->inputs_len,
443 : input);
444 : }
445 : }
446 :
447 : {
448 : const json_t *joutput;
449 : size_t idx;
450 17 : json_array_foreach ((json_t *) joutputs, idx, joutput)
451 : {
452 8 : struct TALER_MERCHANT_ContractOutput output = {
453 : .details.token.count = 1
454 : };
455 :
456 8 : if (GNUNET_OK !=
457 8 : TALER_MERCHANT_parse_choice_output ((json_t *) joutput,
458 : &output,
459 : idx,
460 : false))
461 : {
462 0 : GNUNET_break (0);
463 0 : return GNUNET_SYSERR;
464 : }
465 8 : switch (output.type)
466 : {
467 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
468 0 : GNUNET_break_op (0);
469 0 : return GNUNET_SYSERR;
470 7 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
471 : /* Ignore output tokens with 'count' field set to 0 */
472 7 : if (0 == output.details.token.count)
473 0 : continue;
474 7 : break;
475 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
476 1 : break;
477 : }
478 8 : GNUNET_array_append (choice->outputs,
479 : choice->outputs_len,
480 : output);
481 : }
482 : }
483 : }
484 :
485 8 : return GNUNET_OK;
486 : }
487 :
488 :
489 : /**
490 : * Provide specification to parse given JSON array to contract terms
491 : * choices. All fields from @a choices elements are copied.
492 : *
493 : * @param name name of the choices field in the JSON
494 : * @param[out] choices where the contract choices array has to be written
495 : * @param[out] choices_len length of the @a choices array
496 : */
497 : static struct GNUNET_JSON_Specification
498 8 : spec_choices (
499 : const char *name,
500 : struct TALER_MERCHANT_ContractChoice **choices,
501 : unsigned int *choices_len)
502 : {
503 8 : struct GNUNET_JSON_Specification ret = {
504 : .cls = (void *) choices_len,
505 : .parser = &parse_choices,
506 : .field = name,
507 : .ptr = choices,
508 : };
509 :
510 8 : return ret;
511 : }
512 :
513 :
514 : void
515 19 : TALER_MERCHANT_contract_choice_free (
516 : struct TALER_MERCHANT_ContractChoice *choice)
517 : {
518 35 : for (unsigned int i = 0; i < choice->outputs_len; i++)
519 : {
520 16 : struct TALER_MERCHANT_ContractOutput *output = &choice->outputs[i];
521 :
522 16 : switch (output->type)
523 : {
524 0 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID:
525 0 : GNUNET_break (0);
526 0 : break;
527 15 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN:
528 15 : break;
529 1 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT:
530 1 : for (unsigned int j = 0;
531 4 : j<output->details.donation_receipt.donau_urls_len;
532 3 : j++)
533 3 : GNUNET_free (output->details.donation_receipt.donau_urls[j]);
534 1 : GNUNET_array_grow (output->details.donation_receipt.donau_urls,
535 : output->details.donation_receipt.donau_urls_len,
536 : 0);
537 1 : break;
538 : #if FUTURE
539 : case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_COIN:
540 : GNUNET_free (output->details.coin.exchange_url);
541 : break;
542 : #endif
543 : }
544 : }
545 19 : GNUNET_free (choice->description);
546 19 : if (NULL != choice->description_i18n)
547 : {
548 1 : json_decref (choice->description_i18n);
549 1 : choice->description_i18n = NULL;
550 : }
551 19 : GNUNET_free (choice->inputs);
552 19 : GNUNET_free (choice->outputs);
553 19 : }
554 :
555 :
556 : /**
557 : * Parse token details of the given JSON contract token family.
558 : *
559 : * @param cls closure, unused parameter
560 : * @param root the JSON object representing data
561 : * @param[out] ospec where to write the data
562 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
563 : */
564 : static enum GNUNET_GenericReturnValue
565 9 : parse_token_details (void *cls,
566 : json_t *root,
567 : struct GNUNET_JSON_Specification *ospec)
568 : {
569 9 : struct TALER_MERCHANT_ContractTokenFamily *family = ospec->ptr;
570 : const char *class;
571 : const char *ename;
572 : unsigned int eline;
573 : struct GNUNET_JSON_Specification ispec[] = {
574 9 : GNUNET_JSON_spec_string ("class",
575 : &class),
576 9 : GNUNET_JSON_spec_end ()
577 : };
578 :
579 : (void) cls;
580 9 : if (GNUNET_OK !=
581 9 : GNUNET_JSON_parse (root,
582 : ispec,
583 : &ename,
584 : &eline))
585 : {
586 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
587 : "Failed to parse %s at %u: %s\n",
588 : ispec[eline].field,
589 : eline,
590 : ename);
591 0 : GNUNET_break_op (0);
592 0 : return GNUNET_SYSERR;
593 : }
594 :
595 9 : family->kind = contract_token_kind_from_string (class);
596 :
597 9 : switch (family->kind)
598 : {
599 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
600 0 : break;
601 7 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
602 : {
603 : const json_t *trusted_domains;
604 : struct GNUNET_JSON_Specification spec[] = {
605 7 : GNUNET_JSON_spec_array_const ("trusted_domains",
606 : &trusted_domains),
607 7 : GNUNET_JSON_spec_end ()
608 : };
609 :
610 7 : if (GNUNET_OK !=
611 7 : GNUNET_JSON_parse (root,
612 : spec,
613 : &ename,
614 : &eline))
615 : {
616 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
617 : "Failed to parse %s at %u: %s\n",
618 : spec[eline].field,
619 : eline,
620 : ename);
621 0 : GNUNET_break_op (0);
622 0 : return GNUNET_SYSERR;
623 : }
624 :
625 7 : GNUNET_array_grow (family->details.subscription.trusted_domains,
626 : family->details.subscription.trusted_domains_len,
627 : json_array_size (trusted_domains));
628 :
629 7 : for (unsigned int i = 0;
630 10 : i < family->details.subscription.trusted_domains_len;
631 3 : i++)
632 : {
633 : const json_t *jdomain;
634 3 : jdomain = json_array_get (trusted_domains, i);
635 :
636 3 : if (! json_is_string (jdomain))
637 : {
638 0 : GNUNET_break_op (0);
639 0 : return GNUNET_SYSERR;
640 : }
641 :
642 3 : family->details.subscription.trusted_domains[i] =
643 3 : GNUNET_strdup (json_string_value (jdomain));
644 : }
645 :
646 7 : return GNUNET_OK;
647 : }
648 2 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
649 : {
650 : const json_t *expected_domains;
651 : struct GNUNET_JSON_Specification spec[] = {
652 2 : GNUNET_JSON_spec_array_const ("expected_domains",
653 : &expected_domains),
654 2 : GNUNET_JSON_spec_end ()
655 : };
656 :
657 2 : if (GNUNET_OK !=
658 2 : GNUNET_JSON_parse (root,
659 : spec,
660 : &ename,
661 : &eline))
662 : {
663 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
664 : "Failed to parse %s at %u: %s\n",
665 : spec[eline].field,
666 : eline,
667 : ename);
668 0 : GNUNET_break_op (0);
669 0 : return GNUNET_SYSERR;
670 : }
671 :
672 2 : GNUNET_array_grow (family->details.discount.expected_domains,
673 : family->details.discount.expected_domains_len,
674 : json_array_size (expected_domains));
675 :
676 2 : for (unsigned int i = 0;
677 5 : i < family->details.discount.expected_domains_len;
678 3 : i++)
679 : {
680 : const json_t *jdomain;
681 :
682 3 : jdomain = json_array_get (expected_domains,
683 : i);
684 3 : if (! json_is_string (jdomain))
685 : {
686 0 : GNUNET_break_op (0);
687 0 : return GNUNET_SYSERR;
688 : }
689 :
690 3 : family->details.discount.expected_domains[i] =
691 3 : GNUNET_strdup (json_string_value (jdomain));
692 : }
693 :
694 2 : return GNUNET_OK;
695 : }
696 : }
697 :
698 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
699 : "Field 'type' invalid in token family\n");
700 0 : GNUNET_break_op (0);
701 0 : return GNUNET_SYSERR;
702 : }
703 :
704 :
705 : /**
706 : * Provide specification to parse given JSON object to contract token details
707 : * in the contract token family. All fields from @a family are copied.
708 : *
709 : * @param name name of the token details field in the JSON
710 : * @param[out] family token_family where the token details have to be written
711 : */
712 : static struct GNUNET_JSON_Specification
713 9 : spec_token_details (const char *name,
714 : struct TALER_MERCHANT_ContractTokenFamily *family)
715 : {
716 9 : struct GNUNET_JSON_Specification ret = {
717 : .parser = &parse_token_details,
718 : .field = name,
719 : .ptr = family,
720 : };
721 :
722 9 : return ret;
723 : }
724 :
725 :
726 : /**
727 : * Parse given JSON object to token families array.
728 : *
729 : * @param cls closure, pointer to array length
730 : * @param root the json object representing the token families. The keys are
731 : * the token family slugs.
732 : * @param[out] ospec where to write the data
733 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
734 : */
735 : static enum GNUNET_GenericReturnValue
736 8 : parse_token_families (void *cls,
737 : json_t *root,
738 : struct GNUNET_JSON_Specification *ospec)
739 : {
740 8 : struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr;
741 8 : unsigned int *families_len = cls;
742 : json_t *jfamily;
743 : const char *slug;
744 :
745 8 : if (! json_is_object (root))
746 : {
747 0 : GNUNET_break_op (0);
748 0 : return GNUNET_SYSERR;
749 : }
750 :
751 17 : json_object_foreach (root, slug, jfamily)
752 : {
753 : const json_t *keys;
754 18 : struct TALER_MERCHANT_ContractTokenFamily family = {
755 9 : .slug = GNUNET_strdup (slug)
756 : };
757 : struct GNUNET_JSON_Specification spec[] = {
758 9 : GNUNET_JSON_spec_string_copy ("name",
759 : &family.name),
760 9 : GNUNET_JSON_spec_string_copy ("description",
761 : &family.description),
762 9 : GNUNET_JSON_spec_object_copy ("description_i18n",
763 : &family.description_i18n),
764 9 : GNUNET_JSON_spec_array_const ("keys",
765 : &keys),
766 9 : spec_token_details ("details",
767 : &family),
768 9 : GNUNET_JSON_spec_bool ("critical",
769 : &family.critical),
770 9 : GNUNET_JSON_spec_end ()
771 : };
772 : const char *error_name;
773 : unsigned int error_line;
774 :
775 9 : if (GNUNET_OK !=
776 9 : GNUNET_JSON_parse (jfamily,
777 : spec,
778 : &error_name,
779 : &error_line))
780 : {
781 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
782 : "Failed to parse %s at %u: %s\n",
783 : spec[error_line].field,
784 : error_line,
785 : error_name);
786 0 : GNUNET_break_op (0);
787 0 : return GNUNET_SYSERR;
788 : }
789 :
790 9 : GNUNET_array_grow (family.keys,
791 : family.keys_len,
792 : json_array_size (keys));
793 :
794 17 : for (unsigned int i = 0; i<family.keys_len; i++)
795 : {
796 8 : struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i];
797 : struct GNUNET_JSON_Specification key_spec[] = {
798 8 : TALER_JSON_spec_token_pub (
799 : NULL,
800 : &key->pub),
801 8 : GNUNET_JSON_spec_timestamp (
802 : "signature_validity_start",
803 : &key->valid_after),
804 8 : GNUNET_JSON_spec_timestamp (
805 : "signature_validity_end",
806 : &key->valid_before),
807 8 : GNUNET_JSON_spec_end ()
808 : };
809 : const char *ierror_name;
810 : unsigned int ierror_line;
811 :
812 8 : if (GNUNET_OK !=
813 8 : GNUNET_JSON_parse (json_array_get (keys,
814 : i),
815 : key_spec,
816 : &ierror_name,
817 : &ierror_line))
818 : {
819 0 : GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
820 : "Failed to parse %s at %u: %s\n",
821 : key_spec[ierror_line].field,
822 : ierror_line,
823 : ierror_name);
824 0 : GNUNET_break_op (0);
825 0 : return GNUNET_SYSERR;
826 : }
827 : }
828 :
829 9 : GNUNET_array_append (*families,
830 : *families_len,
831 : family);
832 : }
833 :
834 8 : return GNUNET_OK;
835 : }
836 :
837 :
838 : /**
839 : * Provide specification to parse given JSON array to token families in the
840 : * contract terms. All fields from @a families items are copied.
841 : *
842 : * @param name name of the token families field in the JSON
843 : * @param[out] families where the token families array has to be written
844 : * @param[out] families_len length of the @a families array
845 : */
846 : static struct GNUNET_JSON_Specification
847 8 : spec_token_families (
848 : const char *name,
849 : struct TALER_MERCHANT_ContractTokenFamily **families,
850 : unsigned int *families_len)
851 : {
852 8 : struct GNUNET_JSON_Specification ret = {
853 : .cls = (void *) families_len,
854 : .parser = &parse_token_families,
855 : .field = name,
856 : .ptr = families,
857 : };
858 :
859 8 : return ret;
860 : }
861 :
862 :
863 : enum GNUNET_GenericReturnValue
864 2 : TALER_MERCHANT_find_token_family_key (
865 : const char *slug,
866 : struct GNUNET_TIME_Timestamp valid_after,
867 : const struct TALER_MERCHANT_ContractTokenFamily *families,
868 : unsigned int families_len,
869 : struct TALER_MERCHANT_ContractTokenFamily *family,
870 : struct TALER_MERCHANT_ContractTokenFamilyKey *key)
871 : {
872 3 : for (unsigned int i = 0; i < families_len; i++)
873 : {
874 3 : const struct TALER_MERCHANT_ContractTokenFamily *fami
875 3 : = &families[i];
876 :
877 3 : if (0 != strcmp (fami->slug,
878 : slug))
879 1 : continue;
880 2 : if (NULL != family)
881 2 : *family = *fami;
882 2 : for (unsigned int k = 0; k < fami->keys_len; k++)
883 : {
884 2 : struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k];
885 :
886 2 : if (GNUNET_TIME_timestamp_cmp (ki->valid_after,
887 : ==,
888 : valid_after))
889 : {
890 2 : if (NULL != key)
891 2 : *key = *ki;
892 2 : return GNUNET_OK;
893 : }
894 : }
895 : /* matching family found, but no key. */
896 0 : return GNUNET_NO;
897 : }
898 :
899 : /* no matching family found */
900 0 : return GNUNET_SYSERR;
901 : }
902 :
903 :
904 : /**
905 : * Free all the fields in the given @a family, but not @a family itself, since
906 : * it is normally part of an array.
907 : *
908 : * @param[in] family contract token family to free
909 : */
910 : static void
911 9 : contract_token_family_free (
912 : struct TALER_MERCHANT_ContractTokenFamily *family)
913 : {
914 9 : GNUNET_free (family->slug);
915 9 : GNUNET_free (family->name);
916 9 : GNUNET_free (family->description);
917 9 : if (NULL != family->description_i18n)
918 : {
919 9 : json_decref (family->description_i18n);
920 9 : family->description_i18n = NULL;
921 : }
922 17 : for (unsigned int i = 0; i < family->keys_len; i++)
923 8 : TALER_token_issue_pub_free (&family->keys[i].pub);
924 9 : GNUNET_free (family->keys);
925 :
926 9 : switch (family->kind)
927 : {
928 0 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID:
929 0 : break;
930 2 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT:
931 5 : for (unsigned int i = 0; i < family->details.discount.expected_domains_len;
932 3 : i++)
933 3 : GNUNET_free (family->details.discount.expected_domains[i]);
934 2 : break;
935 7 : case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION:
936 10 : for (unsigned int i = 0; i < family->details.subscription.
937 3 : trusted_domains_len; i++)
938 3 : GNUNET_free (family->details.subscription.trusted_domains[i]);
939 7 : break;
940 : }
941 9 : }
942 :
943 :
944 : /**
945 : * Parse contract version of given JSON contract terms.
946 : *
947 : * @param cls closure, unused parameter
948 : * @param root the JSON object representing data
949 : * @param[out] spec where to write the data
950 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
951 : */
952 : static enum GNUNET_GenericReturnValue
953 161 : parse_contract_version (void *cls,
954 : json_t *root,
955 : struct GNUNET_JSON_Specification *spec)
956 : {
957 161 : enum TALER_MERCHANT_ContractVersion *res
958 : = (enum TALER_MERCHANT_ContractVersion *) spec->ptr;
959 :
960 : (void) cls;
961 161 : if (json_is_integer (root))
962 : {
963 161 : json_int_t version = json_integer_value (root);
964 :
965 161 : switch (version)
966 : {
967 153 : case 0:
968 153 : *res = TALER_MERCHANT_CONTRACT_VERSION_0;
969 153 : return GNUNET_OK;
970 8 : case 1:
971 8 : *res = TALER_MERCHANT_CONTRACT_VERSION_1;
972 8 : return GNUNET_OK;
973 : }
974 :
975 0 : GNUNET_break_op (0);
976 0 : return GNUNET_SYSERR;
977 : }
978 :
979 0 : if (json_is_null (root))
980 : {
981 0 : *res = TALER_MERCHANT_CONTRACT_VERSION_0;
982 0 : return GNUNET_OK;
983 : }
984 :
985 0 : GNUNET_break_op (0);
986 0 : return GNUNET_SYSERR;
987 : }
988 :
989 :
990 : /**
991 : * Create JSON specification to parse a merchant contract
992 : * version.
993 : *
994 : * @param name name of the field
995 : * @param[out] version where to write the contract version
996 : * @return JSON specification object
997 : */
998 : static struct GNUNET_JSON_Specification
999 161 : spec_contract_version (
1000 : const char *name,
1001 : enum TALER_MERCHANT_ContractVersion *version)
1002 : {
1003 161 : struct GNUNET_JSON_Specification ret = {
1004 : .parser = &parse_contract_version,
1005 : .field = name,
1006 : .ptr = version
1007 : };
1008 :
1009 161 : *version = TALER_MERCHANT_CONTRACT_VERSION_0;
1010 161 : return ret;
1011 : }
1012 :
1013 :
1014 : /**
1015 : * Parse v0-specific fields of @a input JSON into @a contract.
1016 : *
1017 : * @param[in] input the JSON contract terms
1018 : * @param[out] contract where to write the data
1019 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1020 : */
1021 : static enum GNUNET_GenericReturnValue
1022 153 : parse_contract_v0 (
1023 : json_t *input,
1024 : struct TALER_MERCHANT_Contract *contract)
1025 : {
1026 : struct GNUNET_JSON_Specification espec[] = {
1027 153 : TALER_JSON_spec_amount_any ("amount",
1028 : &contract->details.v0.brutto),
1029 153 : TALER_JSON_spec_amount_any ("max_fee",
1030 : &contract->details.v0.max_fee),
1031 153 : GNUNET_JSON_spec_end ()
1032 : };
1033 : enum GNUNET_GenericReturnValue res;
1034 : const char *ename;
1035 : unsigned int eline;
1036 :
1037 153 : res = GNUNET_JSON_parse (input,
1038 : espec,
1039 : &ename,
1040 : &eline);
1041 153 : if (GNUNET_OK != res)
1042 : {
1043 0 : GNUNET_break (0);
1044 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1045 : "Failed to parse contract v0 at field %s\n",
1046 : ename);
1047 0 : return GNUNET_SYSERR;
1048 : }
1049 :
1050 153 : if (GNUNET_OK !=
1051 153 : TALER_amount_cmp_currency (&contract->details.v0.max_fee,
1052 153 : &contract->details.v0.brutto))
1053 : {
1054 0 : GNUNET_break (0);
1055 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1056 : "'max_fee' in database does not match currency of contract price");
1057 0 : return GNUNET_SYSERR;
1058 : }
1059 :
1060 153 : return res;
1061 : }
1062 :
1063 :
1064 : /**
1065 : * Parse v1-specific fields of @a input JSON into @a contract.
1066 : *
1067 : * @param[in] input the JSON contract terms
1068 : * @param[out] contract where to write the data
1069 : * @return #GNUNET_OK upon successful parsing; #GNUNET_SYSERR upon error
1070 : */
1071 : static enum GNUNET_GenericReturnValue
1072 8 : parse_contract_v1 (
1073 : json_t *input,
1074 : struct TALER_MERCHANT_Contract *contract)
1075 : {
1076 : struct GNUNET_JSON_Specification espec[] = {
1077 8 : spec_choices (
1078 : "choices",
1079 : &contract->details.v1.choices,
1080 : &contract->details.v1.choices_len),
1081 8 : spec_token_families (
1082 : "token_families",
1083 : &contract->details.v1.token_authorities,
1084 : &contract->details.v1.token_authorities_len),
1085 8 : GNUNET_JSON_spec_end ()
1086 : };
1087 :
1088 : enum GNUNET_GenericReturnValue res;
1089 : const char *ename;
1090 : unsigned int eline;
1091 :
1092 8 : res = GNUNET_JSON_parse (input,
1093 : espec,
1094 : &ename,
1095 : &eline);
1096 8 : if (GNUNET_OK != res)
1097 : {
1098 0 : GNUNET_break (0);
1099 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1100 : "Failed to parse contract v1 at field %s\n",
1101 : ename);
1102 0 : return GNUNET_SYSERR;
1103 : }
1104 :
1105 8 : return res;
1106 : }
1107 :
1108 :
1109 : struct TALER_MERCHANT_Contract *
1110 161 : TALER_MERCHANT_contract_parse (json_t *input,
1111 : bool nonce_optional)
1112 : {
1113 : struct TALER_MERCHANT_Contract *contract
1114 161 : = GNUNET_new (struct TALER_MERCHANT_Contract);
1115 : struct GNUNET_JSON_Specification espec[] = {
1116 161 : spec_contract_version ("version",
1117 : &contract->version),
1118 161 : GNUNET_JSON_spec_string_copy ("summary",
1119 : &contract->summary),
1120 : /* FIXME: do i18n_str validation in the future */
1121 161 : GNUNET_JSON_spec_mark_optional (
1122 : GNUNET_JSON_spec_object_copy ("summary_i18n",
1123 : &contract->summary_i18n),
1124 : NULL),
1125 161 : GNUNET_JSON_spec_string_copy ("order_id",
1126 : &contract->order_id),
1127 161 : GNUNET_JSON_spec_mark_optional (
1128 : GNUNET_JSON_spec_string_copy ("public_reorder_url",
1129 : &contract->public_reorder_url),
1130 : NULL),
1131 161 : GNUNET_JSON_spec_mark_optional (
1132 : GNUNET_JSON_spec_string_copy ("fulfillment_url",
1133 : &contract->fulfillment_url),
1134 : NULL),
1135 161 : GNUNET_JSON_spec_mark_optional (
1136 : GNUNET_JSON_spec_string_copy ("fulfillment_message",
1137 : &contract->fulfillment_message),
1138 : NULL),
1139 161 : GNUNET_JSON_spec_mark_optional (
1140 : GNUNET_JSON_spec_object_copy ("fulfillment_message_i18n",
1141 : &contract->fulfillment_message_i18n),
1142 : NULL),
1143 161 : GNUNET_JSON_spec_array_copy ("products",
1144 : &contract->products),
1145 161 : GNUNET_JSON_spec_timestamp ("timestamp",
1146 : &contract->timestamp),
1147 161 : GNUNET_JSON_spec_timestamp ("refund_deadline",
1148 : &contract->refund_deadline),
1149 161 : GNUNET_JSON_spec_timestamp ("pay_deadline",
1150 : &contract->pay_deadline),
1151 161 : GNUNET_JSON_spec_timestamp ("wire_transfer_deadline",
1152 : &contract->wire_deadline),
1153 161 : GNUNET_JSON_spec_fixed_auto ("merchant_pub",
1154 : &contract->merchant_pub),
1155 161 : GNUNET_JSON_spec_string_copy ("merchant_base_url",
1156 : &contract->merchant_base_url),
1157 161 : spec_merchant_details ("merchant",
1158 : contract),
1159 161 : GNUNET_JSON_spec_fixed_auto ("h_wire",
1160 : &contract->h_wire),
1161 161 : GNUNET_JSON_spec_string_copy ("wire_method",
1162 : &contract->wire_method),
1163 161 : GNUNET_JSON_spec_array_copy ("exchanges",
1164 : &contract->exchanges),
1165 161 : GNUNET_JSON_spec_mark_optional (
1166 : GNUNET_JSON_spec_object_copy ("delivery_location",
1167 : &contract->delivery_location),
1168 : NULL),
1169 161 : GNUNET_JSON_spec_mark_optional (
1170 : GNUNET_JSON_spec_timestamp ("delivery_date",
1171 : &contract->delivery_date),
1172 : NULL),
1173 : (nonce_optional)
1174 159 : ? GNUNET_JSON_spec_mark_optional (
1175 : GNUNET_JSON_spec_string_copy ("nonce",
1176 : &contract->nonce),
1177 : NULL)
1178 161 : : GNUNET_JSON_spec_string_copy ("nonce",
1179 : &contract->nonce),
1180 161 : GNUNET_JSON_spec_mark_optional (
1181 : GNUNET_JSON_spec_relative_time ("auto_refund",
1182 : &contract->auto_refund),
1183 : NULL),
1184 161 : GNUNET_JSON_spec_mark_optional (
1185 : GNUNET_JSON_spec_object_copy ("extra",
1186 : &contract->extra),
1187 : NULL),
1188 161 : GNUNET_JSON_spec_mark_optional (
1189 : GNUNET_JSON_spec_uint8 ("minimum_age",
1190 : &contract->minimum_age),
1191 : NULL),
1192 161 : GNUNET_JSON_spec_end ()
1193 : };
1194 :
1195 : enum GNUNET_GenericReturnValue res;
1196 : const char *ename;
1197 : unsigned int eline;
1198 :
1199 161 : GNUNET_assert (NULL != input);
1200 161 : res = GNUNET_JSON_parse (input,
1201 : espec,
1202 : &ename,
1203 : &eline);
1204 161 : if (GNUNET_OK != res)
1205 : {
1206 0 : GNUNET_break (0);
1207 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1208 : "Failed to parse contract at field %s\n",
1209 : ename);
1210 0 : goto cleanup;
1211 : }
1212 :
1213 161 : switch (contract->version)
1214 : {
1215 153 : case TALER_MERCHANT_CONTRACT_VERSION_0:
1216 153 : res = parse_contract_v0 (input,
1217 : contract);
1218 153 : break;
1219 8 : case TALER_MERCHANT_CONTRACT_VERSION_1:
1220 8 : res = parse_contract_v1 (input,
1221 : contract);
1222 8 : break;
1223 : }
1224 :
1225 161 : if (GNUNET_OK != res)
1226 : {
1227 0 : GNUNET_break (0);
1228 0 : GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
1229 : "Failed to parse contract\n");
1230 0 : goto cleanup;
1231 : }
1232 161 : return contract;
1233 :
1234 0 : cleanup:
1235 0 : TALER_MERCHANT_contract_free (contract);
1236 0 : return NULL;
1237 : }
1238 :
1239 :
1240 : void
1241 161 : TALER_MERCHANT_contract_free (
1242 : struct TALER_MERCHANT_Contract *contract)
1243 : {
1244 161 : if (NULL == contract)
1245 0 : return;
1246 161 : GNUNET_free (contract->public_reorder_url);
1247 161 : GNUNET_free (contract->order_id);
1248 161 : GNUNET_free (contract->merchant_base_url);
1249 161 : GNUNET_free (contract->merchant.name);
1250 161 : GNUNET_free (contract->merchant.website);
1251 161 : GNUNET_free (contract->merchant.email);
1252 161 : GNUNET_free (contract->merchant.logo);
1253 161 : if (NULL != contract->merchant.address)
1254 : {
1255 161 : json_decref (contract->merchant.address);
1256 161 : contract->merchant.address = NULL;
1257 : }
1258 161 : if (NULL != contract->merchant.jurisdiction)
1259 : {
1260 161 : json_decref (contract->merchant.jurisdiction);
1261 161 : contract->merchant.jurisdiction = NULL;
1262 : }
1263 161 : GNUNET_free (contract->summary);
1264 161 : GNUNET_free (contract->fulfillment_url);
1265 161 : GNUNET_free (contract->fulfillment_message);
1266 161 : if (NULL != contract->fulfillment_message_i18n)
1267 : {
1268 2 : json_decref (contract->fulfillment_message_i18n);
1269 2 : contract->fulfillment_message_i18n = NULL;
1270 : }
1271 161 : if (NULL != contract->products)
1272 : {
1273 161 : json_decref (contract->products);
1274 161 : contract->products = NULL;
1275 : }
1276 161 : GNUNET_free (contract->wire_method);
1277 161 : if (NULL != contract->exchanges)
1278 : {
1279 161 : json_decref (contract->exchanges);
1280 161 : contract->exchanges = NULL;
1281 : }
1282 161 : if (NULL != contract->delivery_location)
1283 : {
1284 2 : json_decref (contract->delivery_location);
1285 2 : contract->delivery_location = NULL;
1286 : }
1287 161 : GNUNET_free (contract->nonce);
1288 161 : if (NULL != contract->extra)
1289 : {
1290 2 : json_decref (contract->extra);
1291 2 : contract->extra = NULL;
1292 : }
1293 :
1294 161 : switch (contract->version)
1295 : {
1296 153 : case TALER_MERCHANT_CONTRACT_VERSION_0:
1297 153 : break;
1298 8 : case TALER_MERCHANT_CONTRACT_VERSION_1:
1299 8 : for (unsigned int i = 0;
1300 17 : i < contract->details.v1.choices_len;
1301 9 : i++)
1302 9 : TALER_MERCHANT_contract_choice_free (&contract->details.v1.choices[i]);
1303 8 : GNUNET_free (contract->details.v1.choices);
1304 8 : for (unsigned int i = 0;
1305 17 : i < contract->details.v1.token_authorities_len;
1306 9 : i++)
1307 9 : contract_token_family_free (&contract->details.v1.token_authorities[i]);
1308 8 : GNUNET_free (contract->details.v1.token_authorities);
1309 8 : break;
1310 : }
1311 161 : GNUNET_free (contract);
1312 : }
|