contract_parse.c (38885B)
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 parse_merchant_details (void *cls, 42 json_t *root, 43 struct GNUNET_JSON_Specification *ospec) 44 { 45 struct TALER_MERCHANT_Contract *contract = ospec->ptr; 46 struct GNUNET_JSON_Specification spec[] = { 47 GNUNET_JSON_spec_string_copy ("name", 48 &contract->merchant.name), 49 GNUNET_JSON_spec_mark_optional ( 50 GNUNET_JSON_spec_string_copy ("email", 51 &contract->merchant.email), 52 NULL), 53 GNUNET_JSON_spec_mark_optional ( 54 GNUNET_JSON_spec_string_copy ("website", 55 &contract->merchant.website), 56 NULL), 57 GNUNET_JSON_spec_mark_optional ( 58 GNUNET_JSON_spec_string_copy ("logo", 59 &contract->merchant.logo), 60 NULL), 61 GNUNET_JSON_spec_mark_optional ( 62 GNUNET_JSON_spec_object_copy ("address", 63 &contract->merchant.address), 64 NULL), 65 GNUNET_JSON_spec_mark_optional ( 66 GNUNET_JSON_spec_object_copy ("jurisdiction", 67 &contract->merchant.jurisdiction), 68 NULL), 69 GNUNET_JSON_spec_end () 70 }; 71 const char *error_name; 72 unsigned int error_line; 73 74 (void) cls; 75 if (GNUNET_OK != 76 GNUNET_JSON_parse (root, 77 spec, 78 &error_name, 79 &error_line)) 80 { 81 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 GNUNET_break_op (0); 87 return GNUNET_SYSERR; 88 } 89 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 spec_merchant_details (const char *name, 102 struct TALER_MERCHANT_Contract *contract) 103 { 104 struct GNUNET_JSON_Specification ret = { 105 .parser = &parse_merchant_details, 106 .field = name, 107 .ptr = contract, 108 }; 109 110 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 contract_token_kind_from_string (const char *str) 122 { 123 if (0 == strcmp ("subscription", 124 str)) 125 return TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION; 126 if (0 == strcmp ("discount", 127 str)) 128 return TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT; 129 return TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID; 130 } 131 132 133 enum GNUNET_GenericReturnValue 134 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 TALER_MERCHANT_json_spec_cit ("type", 144 &input->type), 145 GNUNET_JSON_spec_end () 146 }; 147 148 if (GNUNET_OK != 149 GNUNET_JSON_parse (root, 150 ispec, 151 &ename, 152 &eline)) 153 { 154 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 155 "Failed to parse %s at %u: %s\n", 156 ispec[eline].field, 157 eline, 158 ename); 159 GNUNET_break_op (0); 160 return GNUNET_SYSERR; 161 } 162 163 switch (input->type) 164 { 165 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 166 GNUNET_break (0); 167 break; 168 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 169 { 170 struct GNUNET_JSON_Specification spec[] = { 171 GNUNET_JSON_spec_string ("token_family_slug", 172 &input->details.token.token_family_slug), 173 GNUNET_JSON_spec_mark_optional ( 174 GNUNET_JSON_spec_uint32 ("count", 175 &input->details.token.count), 176 NULL), 177 GNUNET_JSON_spec_end () 178 }; 179 180 if (GNUNET_OK != 181 GNUNET_JSON_parse (root, 182 spec, 183 &ename, 184 &eline)) 185 { 186 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 187 "Failed to parse %s at %u: %s\n", 188 spec[eline].field, 189 eline, 190 ename); 191 GNUNET_break_op (0); 192 return GNUNET_SYSERR; 193 } 194 195 return GNUNET_OK; 196 } 197 } 198 199 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 200 "Field 'type' invalid in input #%u\n", 201 (unsigned int) index); 202 GNUNET_break_op (0); 203 return GNUNET_SYSERR; 204 } 205 206 207 enum GNUNET_GenericReturnValue 208 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 TALER_MERCHANT_json_spec_cot ("type", 218 &output->type), 219 GNUNET_JSON_spec_end () 220 }; 221 222 if (GNUNET_OK != 223 GNUNET_JSON_parse (root, 224 ispec, 225 &ename, 226 &eline)) 227 { 228 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 229 "Failed to parse %s at %u: %s\n", 230 ispec[eline].field, 231 eline, 232 ename); 233 GNUNET_break_op (0); 234 return GNUNET_SYSERR; 235 } 236 237 switch (output->type) 238 { 239 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 240 GNUNET_break (0); 241 break; 242 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 243 { 244 struct GNUNET_JSON_Specification spec[] = { 245 GNUNET_JSON_spec_string ("token_family_slug", 246 &output->details.token.token_family_slug), 247 GNUNET_JSON_spec_mark_optional ( 248 GNUNET_JSON_spec_uint ("count", 249 &output->details.token.count), 250 NULL), 251 GNUNET_JSON_spec_mark_optional ( 252 GNUNET_JSON_spec_timestamp ("valid_at", 253 &output->details.token.valid_at), 254 NULL), 255 (! order) 256 ? GNUNET_JSON_spec_uint ("key_index", 257 &output->details.token.key_index) 258 : GNUNET_JSON_spec_end (), 259 GNUNET_JSON_spec_end () 260 }; 261 262 if (GNUNET_OK != 263 GNUNET_JSON_parse (root, 264 spec, 265 &ename, 266 &eline)) 267 { 268 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 269 "Failed to parse %s at %u: %s\n", 270 spec[eline].field, 271 eline, 272 ename); 273 GNUNET_break_op (0); 274 return GNUNET_SYSERR; 275 } 276 277 return GNUNET_OK; 278 } 279 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 280 { 281 const json_t *donau_urls = NULL; 282 struct GNUNET_JSON_Specification spec[] = { 283 GNUNET_JSON_spec_mark_optional ( 284 TALER_JSON_spec_amount_any ("amount", 285 &output->details.donation_receipt.amount), 286 NULL), 287 (! order) 288 ? GNUNET_JSON_spec_array_const ("donau_urls", 289 &donau_urls) 290 : GNUNET_JSON_spec_end (), 291 GNUNET_JSON_spec_end () 292 }; 293 294 if (GNUNET_OK != 295 GNUNET_JSON_parse (root, 296 spec, 297 &ename, 298 &eline)) 299 { 300 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 301 "Failed to parse %s at %u: %s\n", 302 spec[eline].field, 303 eline, 304 ename); 305 GNUNET_break_op (0); 306 return GNUNET_SYSERR; 307 } 308 309 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 for (unsigned int i = 0; 314 i < output->details.donation_receipt.donau_urls_len; 315 i++) 316 { 317 const json_t *jurl; 318 319 jurl = json_array_get (donau_urls, 320 i); 321 if (! json_is_string (jurl)) 322 { 323 GNUNET_break_op (0); 324 return GNUNET_SYSERR; 325 } 326 output->details.donation_receipt.donau_urls[i] = 327 GNUNET_strdup (json_string_value (jurl)); 328 } 329 330 return GNUNET_OK; 331 } 332 } 333 334 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 335 "Field 'type' invalid in output #%u\n", 336 (unsigned int) index); 337 GNUNET_break_op (0); 338 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 parse_choices ( 352 void *cls, 353 json_t *root, 354 struct GNUNET_JSON_Specification *ospec) 355 { 356 struct TALER_MERCHANT_ContractChoice **choices = ospec->ptr; 357 unsigned int *choices_len = cls; 358 359 if (! json_is_array (root)) 360 { 361 GNUNET_break_op (0); 362 return GNUNET_SYSERR; 363 } 364 365 GNUNET_array_grow (*choices, 366 *choices_len, 367 json_array_size (root)); 368 369 for (unsigned int i = 0; i < *choices_len; i++) 370 { 371 struct TALER_MERCHANT_ContractChoice *choice = &(*choices)[i]; 372 const json_t *jinputs; 373 const json_t *joutputs; 374 struct GNUNET_JSON_Specification spec[] = { 375 TALER_JSON_spec_amount_any ("amount", 376 &choice->amount), 377 GNUNET_JSON_spec_mark_optional ( 378 GNUNET_JSON_spec_string_copy ("description", 379 &choice->description), 380 NULL), 381 GNUNET_JSON_spec_mark_optional ( 382 GNUNET_JSON_spec_object_copy ("description_i18n", 383 &choice->description_i18n), 384 NULL), 385 TALER_JSON_spec_amount_any ("max_fee", 386 &choice->max_fee), 387 GNUNET_JSON_spec_array_const ("inputs", 388 &jinputs), 389 GNUNET_JSON_spec_array_const ("outputs", 390 &joutputs), 391 GNUNET_JSON_spec_end () 392 }; 393 const char *ename; 394 unsigned int eline; 395 396 if (GNUNET_OK != 397 GNUNET_JSON_parse (json_array_get (root, i), 398 spec, 399 &ename, 400 &eline)) 401 { 402 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 403 "Failed to parse %s at %u: %s\n", 404 spec[eline].field, 405 eline, 406 ename); 407 GNUNET_break_op (0); 408 return GNUNET_SYSERR; 409 } 410 411 { 412 const json_t *jinput; 413 size_t idx; 414 415 json_array_foreach ((json_t *) jinputs, idx, jinput) 416 { 417 struct TALER_MERCHANT_ContractInput input = { 418 .details.token.count = 1 419 }; 420 421 if (GNUNET_OK != 422 TALER_MERCHANT_parse_choice_input ((json_t *) jinput, 423 &input, 424 idx, 425 false)) 426 { 427 GNUNET_break (0); 428 return GNUNET_SYSERR; 429 } 430 switch (input.type) 431 { 432 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_INVALID: 433 GNUNET_break_op (0); 434 return GNUNET_SYSERR; 435 case TALER_MERCHANT_CONTRACT_INPUT_TYPE_TOKEN: 436 /* Ignore inputs tokens with 'count' field set to 0 */ 437 if (0 == input.details.token.count) 438 continue; 439 break; 440 } 441 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 json_array_foreach ((json_t *) joutputs, idx, joutput) 451 { 452 struct TALER_MERCHANT_ContractOutput output = { 453 .details.token.count = 1 454 }; 455 456 if (GNUNET_OK != 457 TALER_MERCHANT_parse_choice_output ((json_t *) joutput, 458 &output, 459 idx, 460 false)) 461 { 462 GNUNET_break (0); 463 return GNUNET_SYSERR; 464 } 465 switch (output.type) 466 { 467 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 468 GNUNET_break_op (0); 469 return GNUNET_SYSERR; 470 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 471 /* Ignore output tokens with 'count' field set to 0 */ 472 if (0 == output.details.token.count) 473 continue; 474 break; 475 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 476 break; 477 } 478 GNUNET_array_append (choice->outputs, 479 choice->outputs_len, 480 output); 481 } 482 } 483 } 484 485 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 spec_choices ( 499 const char *name, 500 struct TALER_MERCHANT_ContractChoice **choices, 501 unsigned int *choices_len) 502 { 503 struct GNUNET_JSON_Specification ret = { 504 .cls = (void *) choices_len, 505 .parser = &parse_choices, 506 .field = name, 507 .ptr = choices, 508 }; 509 510 return ret; 511 } 512 513 514 void 515 TALER_MERCHANT_contract_choice_free ( 516 struct TALER_MERCHANT_ContractChoice *choice) 517 { 518 for (unsigned int i = 0; i < choice->outputs_len; i++) 519 { 520 struct TALER_MERCHANT_ContractOutput *output = &choice->outputs[i]; 521 522 switch (output->type) 523 { 524 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_INVALID: 525 GNUNET_break (0); 526 break; 527 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_TOKEN: 528 break; 529 case TALER_MERCHANT_CONTRACT_OUTPUT_TYPE_DONATION_RECEIPT: 530 for (unsigned int j = 0; 531 j<output->details.donation_receipt.donau_urls_len; 532 j++) 533 GNUNET_free (output->details.donation_receipt.donau_urls[j]); 534 GNUNET_array_grow (output->details.donation_receipt.donau_urls, 535 output->details.donation_receipt.donau_urls_len, 536 0); 537 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 GNUNET_free (choice->description); 546 if (NULL != choice->description_i18n) 547 { 548 json_decref (choice->description_i18n); 549 choice->description_i18n = NULL; 550 } 551 GNUNET_free (choice->inputs); 552 GNUNET_free (choice->outputs); 553 } 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 parse_token_details (void *cls, 566 json_t *root, 567 struct GNUNET_JSON_Specification *ospec) 568 { 569 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 GNUNET_JSON_spec_string ("class", 575 &class), 576 GNUNET_JSON_spec_end () 577 }; 578 579 (void) cls; 580 if (GNUNET_OK != 581 GNUNET_JSON_parse (root, 582 ispec, 583 &ename, 584 &eline)) 585 { 586 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 587 "Failed to parse %s at %u: %s\n", 588 ispec[eline].field, 589 eline, 590 ename); 591 GNUNET_break_op (0); 592 return GNUNET_SYSERR; 593 } 594 595 family->kind = contract_token_kind_from_string (class); 596 597 switch (family->kind) 598 { 599 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID: 600 break; 601 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION: 602 { 603 const json_t *trusted_domains; 604 struct GNUNET_JSON_Specification spec[] = { 605 GNUNET_JSON_spec_array_const ("trusted_domains", 606 &trusted_domains), 607 GNUNET_JSON_spec_end () 608 }; 609 610 if (GNUNET_OK != 611 GNUNET_JSON_parse (root, 612 spec, 613 &ename, 614 &eline)) 615 { 616 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 617 "Failed to parse %s at %u: %s\n", 618 spec[eline].field, 619 eline, 620 ename); 621 GNUNET_break_op (0); 622 return GNUNET_SYSERR; 623 } 624 625 GNUNET_array_grow (family->details.subscription.trusted_domains, 626 family->details.subscription.trusted_domains_len, 627 json_array_size (trusted_domains)); 628 629 for (unsigned int i = 0; 630 i < family->details.subscription.trusted_domains_len; 631 i++) 632 { 633 const json_t *jdomain; 634 jdomain = json_array_get (trusted_domains, i); 635 636 if (! json_is_string (jdomain)) 637 { 638 GNUNET_break_op (0); 639 return GNUNET_SYSERR; 640 } 641 642 family->details.subscription.trusted_domains[i] = 643 GNUNET_strdup (json_string_value (jdomain)); 644 } 645 646 return GNUNET_OK; 647 } 648 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT: 649 { 650 const json_t *expected_domains; 651 struct GNUNET_JSON_Specification spec[] = { 652 GNUNET_JSON_spec_array_const ("expected_domains", 653 &expected_domains), 654 GNUNET_JSON_spec_end () 655 }; 656 657 if (GNUNET_OK != 658 GNUNET_JSON_parse (root, 659 spec, 660 &ename, 661 &eline)) 662 { 663 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 664 "Failed to parse %s at %u: %s\n", 665 spec[eline].field, 666 eline, 667 ename); 668 GNUNET_break_op (0); 669 return GNUNET_SYSERR; 670 } 671 672 GNUNET_array_grow (family->details.discount.expected_domains, 673 family->details.discount.expected_domains_len, 674 json_array_size (expected_domains)); 675 676 for (unsigned int i = 0; 677 i < family->details.discount.expected_domains_len; 678 i++) 679 { 680 const json_t *jdomain; 681 682 jdomain = json_array_get (expected_domains, 683 i); 684 if (! json_is_string (jdomain)) 685 { 686 GNUNET_break_op (0); 687 return GNUNET_SYSERR; 688 } 689 690 family->details.discount.expected_domains[i] = 691 GNUNET_strdup (json_string_value (jdomain)); 692 } 693 694 return GNUNET_OK; 695 } 696 } 697 698 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 699 "Field 'type' invalid in token family\n"); 700 GNUNET_break_op (0); 701 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 spec_token_details (const char *name, 714 struct TALER_MERCHANT_ContractTokenFamily *family) 715 { 716 struct GNUNET_JSON_Specification ret = { 717 .parser = &parse_token_details, 718 .field = name, 719 .ptr = family, 720 }; 721 722 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 parse_token_families (void *cls, 737 json_t *root, 738 struct GNUNET_JSON_Specification *ospec) 739 { 740 struct TALER_MERCHANT_ContractTokenFamily **families = ospec->ptr; 741 unsigned int *families_len = cls; 742 json_t *jfamily; 743 const char *slug; 744 745 if (! json_is_object (root)) 746 { 747 GNUNET_break_op (0); 748 return GNUNET_SYSERR; 749 } 750 751 json_object_foreach (root, slug, jfamily) 752 { 753 const json_t *keys; 754 struct TALER_MERCHANT_ContractTokenFamily family = { 755 .slug = GNUNET_strdup (slug) 756 }; 757 struct GNUNET_JSON_Specification spec[] = { 758 GNUNET_JSON_spec_string_copy ("name", 759 &family.name), 760 GNUNET_JSON_spec_string_copy ("description", 761 &family.description), 762 GNUNET_JSON_spec_object_copy ("description_i18n", 763 &family.description_i18n), 764 GNUNET_JSON_spec_array_const ("keys", 765 &keys), 766 spec_token_details ("details", 767 &family), 768 GNUNET_JSON_spec_bool ("critical", 769 &family.critical), 770 GNUNET_JSON_spec_end () 771 }; 772 const char *error_name; 773 unsigned int error_line; 774 775 if (GNUNET_OK != 776 GNUNET_JSON_parse (jfamily, 777 spec, 778 &error_name, 779 &error_line)) 780 { 781 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 GNUNET_break_op (0); 787 return GNUNET_SYSERR; 788 } 789 790 GNUNET_array_grow (family.keys, 791 family.keys_len, 792 json_array_size (keys)); 793 794 for (unsigned int i = 0; i<family.keys_len; i++) 795 { 796 struct TALER_MERCHANT_ContractTokenFamilyKey *key = &family.keys[i]; 797 struct GNUNET_JSON_Specification key_spec[] = { 798 TALER_JSON_spec_token_pub ( 799 NULL, 800 &key->pub), 801 GNUNET_JSON_spec_timestamp ( 802 "signature_validity_start", 803 &key->valid_after), 804 GNUNET_JSON_spec_timestamp ( 805 "signature_validity_end", 806 &key->valid_before), 807 GNUNET_JSON_spec_end () 808 }; 809 const char *ierror_name; 810 unsigned int ierror_line; 811 812 if (GNUNET_OK != 813 GNUNET_JSON_parse (json_array_get (keys, 814 i), 815 key_spec, 816 &ierror_name, 817 &ierror_line)) 818 { 819 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 GNUNET_break_op (0); 825 return GNUNET_SYSERR; 826 } 827 } 828 829 GNUNET_array_append (*families, 830 *families_len, 831 family); 832 } 833 834 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 spec_token_families ( 848 const char *name, 849 struct TALER_MERCHANT_ContractTokenFamily **families, 850 unsigned int *families_len) 851 { 852 struct GNUNET_JSON_Specification ret = { 853 .cls = (void *) families_len, 854 .parser = &parse_token_families, 855 .field = name, 856 .ptr = families, 857 }; 858 859 return ret; 860 } 861 862 863 enum GNUNET_GenericReturnValue 864 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 for (unsigned int i = 0; i < families_len; i++) 873 { 874 const struct TALER_MERCHANT_ContractTokenFamily *fami 875 = &families[i]; 876 877 if (0 != strcmp (fami->slug, 878 slug)) 879 continue; 880 if (NULL != family) 881 *family = *fami; 882 for (unsigned int k = 0; k < fami->keys_len; k++) 883 { 884 struct TALER_MERCHANT_ContractTokenFamilyKey *ki = &fami->keys[k]; 885 886 if (GNUNET_TIME_timestamp_cmp (ki->valid_after, 887 ==, 888 valid_after)) 889 { 890 if (NULL != key) 891 *key = *ki; 892 return GNUNET_OK; 893 } 894 } 895 /* matching family found, but no key. */ 896 return GNUNET_NO; 897 } 898 899 /* no matching family found */ 900 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 contract_token_family_free ( 912 struct TALER_MERCHANT_ContractTokenFamily *family) 913 { 914 GNUNET_free (family->slug); 915 GNUNET_free (family->name); 916 GNUNET_free (family->description); 917 if (NULL != family->description_i18n) 918 { 919 json_decref (family->description_i18n); 920 family->description_i18n = NULL; 921 } 922 for (unsigned int i = 0; i < family->keys_len; i++) 923 TALER_token_issue_pub_free (&family->keys[i].pub); 924 GNUNET_free (family->keys); 925 926 switch (family->kind) 927 { 928 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_INVALID: 929 break; 930 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_DISCOUNT: 931 for (unsigned int i = 0; i < family->details.discount.expected_domains_len; 932 i++) 933 GNUNET_free (family->details.discount.expected_domains[i]); 934 break; 935 case TALER_MERCHANT_CONTRACT_TOKEN_KIND_SUBSCRIPTION: 936 for (unsigned int i = 0; i < family->details.subscription. 937 trusted_domains_len; i++) 938 GNUNET_free (family->details.subscription.trusted_domains[i]); 939 break; 940 } 941 } 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 parse_contract_version (void *cls, 954 json_t *root, 955 struct GNUNET_JSON_Specification *spec) 956 { 957 enum TALER_MERCHANT_ContractVersion *res 958 = (enum TALER_MERCHANT_ContractVersion *) spec->ptr; 959 960 (void) cls; 961 if (json_is_integer (root)) 962 { 963 json_int_t version = json_integer_value (root); 964 965 switch (version) 966 { 967 case 0: 968 *res = TALER_MERCHANT_CONTRACT_VERSION_0; 969 return GNUNET_OK; 970 case 1: 971 *res = TALER_MERCHANT_CONTRACT_VERSION_1; 972 return GNUNET_OK; 973 } 974 975 GNUNET_break_op (0); 976 return GNUNET_SYSERR; 977 } 978 979 if (json_is_null (root)) 980 { 981 *res = TALER_MERCHANT_CONTRACT_VERSION_0; 982 return GNUNET_OK; 983 } 984 985 GNUNET_break_op (0); 986 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 spec_contract_version ( 1000 const char *name, 1001 enum TALER_MERCHANT_ContractVersion *version) 1002 { 1003 struct GNUNET_JSON_Specification ret = { 1004 .parser = &parse_contract_version, 1005 .field = name, 1006 .ptr = version 1007 }; 1008 1009 *version = TALER_MERCHANT_CONTRACT_VERSION_0; 1010 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 parse_contract_v0 ( 1023 json_t *input, 1024 struct TALER_MERCHANT_Contract *contract) 1025 { 1026 struct GNUNET_JSON_Specification espec[] = { 1027 TALER_JSON_spec_amount_any ("amount", 1028 &contract->details.v0.brutto), 1029 TALER_JSON_spec_amount_any ("max_fee", 1030 &contract->details.v0.max_fee), 1031 GNUNET_JSON_spec_end () 1032 }; 1033 enum GNUNET_GenericReturnValue res; 1034 const char *ename; 1035 unsigned int eline; 1036 1037 res = GNUNET_JSON_parse (input, 1038 espec, 1039 &ename, 1040 &eline); 1041 if (GNUNET_OK != res) 1042 { 1043 GNUNET_break (0); 1044 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1045 "Failed to parse contract v0 at field %s\n", 1046 ename); 1047 return GNUNET_SYSERR; 1048 } 1049 1050 if (GNUNET_OK != 1051 TALER_amount_cmp_currency (&contract->details.v0.max_fee, 1052 &contract->details.v0.brutto)) 1053 { 1054 GNUNET_break (0); 1055 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1056 "'max_fee' in database does not match currency of contract price"); 1057 return GNUNET_SYSERR; 1058 } 1059 1060 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 parse_contract_v1 ( 1073 json_t *input, 1074 struct TALER_MERCHANT_Contract *contract) 1075 { 1076 struct GNUNET_JSON_Specification espec[] = { 1077 spec_choices ( 1078 "choices", 1079 &contract->details.v1.choices, 1080 &contract->details.v1.choices_len), 1081 spec_token_families ( 1082 "token_families", 1083 &contract->details.v1.token_authorities, 1084 &contract->details.v1.token_authorities_len), 1085 GNUNET_JSON_spec_end () 1086 }; 1087 1088 enum GNUNET_GenericReturnValue res; 1089 const char *ename; 1090 unsigned int eline; 1091 1092 res = GNUNET_JSON_parse (input, 1093 espec, 1094 &ename, 1095 &eline); 1096 if (GNUNET_OK != res) 1097 { 1098 GNUNET_break (0); 1099 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1100 "Failed to parse contract v1 at field %s\n", 1101 ename); 1102 return GNUNET_SYSERR; 1103 } 1104 1105 return res; 1106 } 1107 1108 1109 struct TALER_MERCHANT_Contract * 1110 TALER_MERCHANT_contract_parse (json_t *input, 1111 bool nonce_optional) 1112 { 1113 struct TALER_MERCHANT_Contract *contract 1114 = GNUNET_new (struct TALER_MERCHANT_Contract); 1115 struct GNUNET_JSON_Specification espec[] = { 1116 spec_contract_version ("version", 1117 &contract->version), 1118 GNUNET_JSON_spec_string_copy ("summary", 1119 &contract->summary), 1120 /* FIXME: do i18n_str validation in the future */ 1121 GNUNET_JSON_spec_mark_optional ( 1122 GNUNET_JSON_spec_object_copy ("summary_i18n", 1123 &contract->summary_i18n), 1124 NULL), 1125 GNUNET_JSON_spec_string_copy ("order_id", 1126 &contract->order_id), 1127 GNUNET_JSON_spec_mark_optional ( 1128 GNUNET_JSON_spec_string_copy ("public_reorder_url", 1129 &contract->public_reorder_url), 1130 NULL), 1131 GNUNET_JSON_spec_mark_optional ( 1132 GNUNET_JSON_spec_string_copy ("fulfillment_url", 1133 &contract->fulfillment_url), 1134 NULL), 1135 GNUNET_JSON_spec_mark_optional ( 1136 GNUNET_JSON_spec_string_copy ("fulfillment_message", 1137 &contract->fulfillment_message), 1138 NULL), 1139 GNUNET_JSON_spec_mark_optional ( 1140 GNUNET_JSON_spec_object_copy ("fulfillment_message_i18n", 1141 &contract->fulfillment_message_i18n), 1142 NULL), 1143 GNUNET_JSON_spec_array_copy ("products", 1144 &contract->products), 1145 GNUNET_JSON_spec_timestamp ("timestamp", 1146 &contract->timestamp), 1147 GNUNET_JSON_spec_timestamp ("refund_deadline", 1148 &contract->refund_deadline), 1149 GNUNET_JSON_spec_timestamp ("pay_deadline", 1150 &contract->pay_deadline), 1151 GNUNET_JSON_spec_timestamp ("wire_transfer_deadline", 1152 &contract->wire_deadline), 1153 GNUNET_JSON_spec_fixed_auto ("merchant_pub", 1154 &contract->merchant_pub), 1155 GNUNET_JSON_spec_string_copy ("merchant_base_url", 1156 &contract->merchant_base_url), 1157 spec_merchant_details ("merchant", 1158 contract), 1159 GNUNET_JSON_spec_fixed_auto ("h_wire", 1160 &contract->h_wire), 1161 GNUNET_JSON_spec_string_copy ("wire_method", 1162 &contract->wire_method), 1163 GNUNET_JSON_spec_array_copy ("exchanges", 1164 &contract->exchanges), 1165 GNUNET_JSON_spec_mark_optional ( 1166 GNUNET_JSON_spec_object_copy ("delivery_location", 1167 &contract->delivery_location), 1168 NULL), 1169 GNUNET_JSON_spec_mark_optional ( 1170 GNUNET_JSON_spec_timestamp ("delivery_date", 1171 &contract->delivery_date), 1172 NULL), 1173 (nonce_optional) 1174 ? GNUNET_JSON_spec_mark_optional ( 1175 GNUNET_JSON_spec_string_copy ("nonce", 1176 &contract->nonce), 1177 NULL) 1178 : GNUNET_JSON_spec_string_copy ("nonce", 1179 &contract->nonce), 1180 GNUNET_JSON_spec_mark_optional ( 1181 GNUNET_JSON_spec_relative_time ("auto_refund", 1182 &contract->auto_refund), 1183 NULL), 1184 GNUNET_JSON_spec_mark_optional ( 1185 GNUNET_JSON_spec_object_copy ("extra", 1186 &contract->extra), 1187 NULL), 1188 GNUNET_JSON_spec_mark_optional ( 1189 GNUNET_JSON_spec_uint8 ("minimum_age", 1190 &contract->minimum_age), 1191 NULL), 1192 GNUNET_JSON_spec_end () 1193 }; 1194 1195 enum GNUNET_GenericReturnValue res; 1196 const char *ename; 1197 unsigned int eline; 1198 1199 GNUNET_assert (NULL != input); 1200 res = GNUNET_JSON_parse (input, 1201 espec, 1202 &ename, 1203 &eline); 1204 if (GNUNET_OK != res) 1205 { 1206 GNUNET_break (0); 1207 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1208 "Failed to parse contract at field %s\n", 1209 ename); 1210 goto cleanup; 1211 } 1212 1213 switch (contract->version) 1214 { 1215 case TALER_MERCHANT_CONTRACT_VERSION_0: 1216 res = parse_contract_v0 (input, 1217 contract); 1218 break; 1219 case TALER_MERCHANT_CONTRACT_VERSION_1: 1220 res = parse_contract_v1 (input, 1221 contract); 1222 break; 1223 } 1224 1225 if (GNUNET_OK != res) 1226 { 1227 GNUNET_break (0); 1228 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1229 "Failed to parse contract\n"); 1230 goto cleanup; 1231 } 1232 return contract; 1233 1234 cleanup: 1235 TALER_MERCHANT_contract_free (contract); 1236 return NULL; 1237 } 1238 1239 1240 void 1241 TALER_MERCHANT_contract_free ( 1242 struct TALER_MERCHANT_Contract *contract) 1243 { 1244 if (NULL == contract) 1245 return; 1246 GNUNET_free (contract->public_reorder_url); 1247 GNUNET_free (contract->order_id); 1248 GNUNET_free (contract->merchant_base_url); 1249 GNUNET_free (contract->merchant.name); 1250 GNUNET_free (contract->merchant.website); 1251 GNUNET_free (contract->merchant.email); 1252 GNUNET_free (contract->merchant.logo); 1253 if (NULL != contract->merchant.address) 1254 { 1255 json_decref (contract->merchant.address); 1256 contract->merchant.address = NULL; 1257 } 1258 if (NULL != contract->merchant.jurisdiction) 1259 { 1260 json_decref (contract->merchant.jurisdiction); 1261 contract->merchant.jurisdiction = NULL; 1262 } 1263 GNUNET_free (contract->summary); 1264 GNUNET_free (contract->fulfillment_url); 1265 GNUNET_free (contract->fulfillment_message); 1266 if (NULL != contract->fulfillment_message_i18n) 1267 { 1268 json_decref (contract->fulfillment_message_i18n); 1269 contract->fulfillment_message_i18n = NULL; 1270 } 1271 if (NULL != contract->products) 1272 { 1273 json_decref (contract->products); 1274 contract->products = NULL; 1275 } 1276 GNUNET_free (contract->wire_method); 1277 if (NULL != contract->exchanges) 1278 { 1279 json_decref (contract->exchanges); 1280 contract->exchanges = NULL; 1281 } 1282 if (NULL != contract->delivery_location) 1283 { 1284 json_decref (contract->delivery_location); 1285 contract->delivery_location = NULL; 1286 } 1287 GNUNET_free (contract->nonce); 1288 if (NULL != contract->extra) 1289 { 1290 json_decref (contract->extra); 1291 contract->extra = NULL; 1292 } 1293 1294 switch (contract->version) 1295 { 1296 case TALER_MERCHANT_CONTRACT_VERSION_0: 1297 break; 1298 case TALER_MERCHANT_CONTRACT_VERSION_1: 1299 for (unsigned int i = 0; 1300 i < contract->details.v1.choices_len; 1301 i++) 1302 TALER_MERCHANT_contract_choice_free (&contract->details.v1.choices[i]); 1303 GNUNET_free (contract->details.v1.choices); 1304 for (unsigned int i = 0; 1305 i < contract->details.v1.token_authorities_len; 1306 i++) 1307 contract_token_family_free (&contract->details.v1.token_authorities[i]); 1308 GNUNET_free (contract->details.v1.token_authorities); 1309 break; 1310 } 1311 GNUNET_free (contract); 1312 }