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