taler-exchange-offline.c (163899B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2020-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 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 taler-exchange-offline.c 18 * @brief Support for operations involving the exchange's offline master key. 19 * @author Christian Grothoff 20 */ 21 #include "taler/platform.h" 22 #include <gnunet/gnunet_json_lib.h> 23 #include <gnunet/gnunet_util_lib.h> 24 #include "taler/taler_json_lib.h" 25 #include "taler/taler_exchange_service.h" 26 #include "taler/taler_extensions.h" 27 #include <regex.h> 28 29 30 /** 31 * Name of the input for the 'sign' and 'show' operation. 32 * The last component --by convention-- identifies the protocol version 33 * and should be incremented whenever the JSON format of the 'argument' changes. 34 */ 35 #define OP_INPUT_KEYS "exchange-input-keys-0" 36 37 /** 38 * Name of the operation to 'disable auditor' 39 * The last component --by convention-- identifies the protocol version 40 * and should be incremented whenever the JSON format of the 'argument' changes. 41 */ 42 #define OP_DISABLE_AUDITOR "exchange-disable-auditor-0" 43 44 /** 45 * Name of the operation to 'enable auditor' 46 * The last component --by convention-- identifies the protocol version 47 * and should be incremented whenever the JSON format of the 'argument' changes. 48 */ 49 #define OP_ENABLE_AUDITOR "exchange-enable-auditor-0" 50 51 /** 52 * Name of the operation to 'enable wire' 53 * The last component --by convention-- identifies the protocol version 54 * and should be incremented whenever the JSON format of the 'argument' changes. 55 */ 56 #define OP_ENABLE_WIRE "exchange-enable-wire-0" 57 58 /** 59 * Name of the operation to 'disable wire' 60 * The last component --by convention-- identifies the protocol version 61 * and should be incremented whenever the JSON format of the 'argument' changes. 62 */ 63 #define OP_DISABLE_WIRE "exchange-disable-wire-0" 64 65 /** 66 * Name of the operation to set a 'wire-fee' 67 * The last component --by convention-- identifies the protocol version 68 * and should be incremented whenever the JSON format of the 'argument' changes. 69 */ 70 #define OP_SET_WIRE_FEE "exchange-set-wire-fee-0" 71 72 /** 73 * Name of the operation to set a 'global-fee' 74 * The last component --by convention-- identifies the protocol version 75 * and should be incremented whenever the JSON format of the 'argument' changes. 76 */ 77 #define OP_SET_GLOBAL_FEE "exchange-set-global-fee-0" 78 79 /** 80 * Name of the operation to 'upload' key signatures 81 * The last component --by convention-- identifies the protocol version 82 * and should be incremented whenever the JSON format of the 'argument' changes. 83 */ 84 #define OP_UPLOAD_SIGS "exchange-upload-sigs-0" 85 86 /** 87 * Name of the operation to 'revoke-denomination' key 88 * The last component --by convention-- identifies the protocol version 89 * and should be incremented whenever the JSON format of the 'argument' changes. 90 */ 91 #define OP_REVOKE_DENOMINATION "exchange-revoke-denomination-0" 92 93 /** 94 * Name of the operation to 'revoke-signkey' 95 * The last component --by convention-- identifies the protocol version 96 * and should be incremented whenever the JSON format of the 'argument' changes. 97 */ 98 #define OP_REVOKE_SIGNKEY "exchange-revoke-signkey-0" 99 100 /** 101 * Show the offline signing key. 102 * The last component --by convention-- identifies the protocol version 103 * and should be incremented whenever the JSON format of the 'argument' changes. 104 */ 105 #define OP_SETUP "exchange-setup-0" 106 107 /** 108 * sign the enabled and configured extensions. 109 */ 110 #define OP_EXTENSIONS "exchange-extensions-0" 111 112 /** 113 * Generate message to drain profits. 114 */ 115 #define OP_DRAIN_PROFITS "exchange-drain-profits-0" 116 117 /** 118 * Setup AML staff. 119 */ 120 #define OP_UPDATE_AML_STAFF "exchange-add-aml-staff-0" 121 122 /** 123 * Setup partner exchange for wad transfers. 124 */ 125 #define OP_ADD_PARTNER "exchange-add-partner-0" 126 127 /** 128 * Our private key, initialized in #load_offline_key(). 129 */ 130 static struct TALER_MasterPrivateKeyP master_priv; 131 132 /** 133 * Our public key, initialized in #load_offline_key(). 134 */ 135 static struct TALER_MasterPublicKeyP master_pub; 136 137 /** 138 * Our context for making HTTP requests. 139 */ 140 static struct GNUNET_CURL_Context *ctx; 141 142 /** 143 * Reschedule context for #ctx. 144 */ 145 static struct GNUNET_CURL_RescheduleContext *rc; 146 147 /** 148 * Handle to the exchange's configuration 149 */ 150 static const struct GNUNET_CONFIGURATION_Handle *kcfg; 151 152 /** 153 * Age restriction configuration 154 */ 155 static bool ar_enabled = false; 156 static struct TALER_AgeRestrictionConfig ar_config = {0}; 157 158 /** 159 * Return value from main(). 160 */ 161 static int global_ret; 162 163 /** 164 * Input to consume. 165 */ 166 static json_t *in; 167 168 /** 169 * Array of actions to perform. 170 */ 171 static json_t *out; 172 173 /** 174 * Currency we have configured. 175 */ 176 static char *currency; 177 178 /** 179 * URL of the exchange we are interacting with 180 * as per our configuration. 181 */ 182 static char *CFG_exchange_url; 183 184 /** 185 * A subcommand supported by this program. 186 */ 187 struct SubCommand 188 { 189 /** 190 * Name of the command. 191 */ 192 const char *name; 193 194 /** 195 * Help text for the command. 196 */ 197 const char *help; 198 199 /** 200 * Function implementing the command. 201 * 202 * @param args subsequent command line arguments (char **) 203 */ 204 void (*cb)(char *const *args); 205 }; 206 207 208 /** 209 * Data structure for denomination revocation requests. 210 */ 211 struct DenomRevocationRequest 212 { 213 214 /** 215 * Kept in a DLL. 216 */ 217 struct DenomRevocationRequest *next; 218 219 /** 220 * Kept in a DLL. 221 */ 222 struct DenomRevocationRequest *prev; 223 224 /** 225 * Operation handle. 226 */ 227 struct TALER_EXCHANGE_ManagementRevokeDenominationKeyHandle *h; 228 229 /** 230 * Array index of the associated command. 231 */ 232 size_t idx; 233 }; 234 235 236 /** 237 * Data structure for signkey revocation requests. 238 */ 239 struct SignkeyRevocationRequest 240 { 241 242 /** 243 * Kept in a DLL. 244 */ 245 struct SignkeyRevocationRequest *next; 246 247 /** 248 * Kept in a DLL. 249 */ 250 struct SignkeyRevocationRequest *prev; 251 252 /** 253 * Operation handle. 254 */ 255 struct TALER_EXCHANGE_ManagementRevokeSigningKeyHandle *h; 256 257 /** 258 * Array index of the associated command. 259 */ 260 size_t idx; 261 }; 262 263 264 /** 265 * Data structure for auditor add requests. 266 */ 267 struct AuditorAddRequest 268 { 269 270 /** 271 * Kept in a DLL. 272 */ 273 struct AuditorAddRequest *next; 274 275 /** 276 * Kept in a DLL. 277 */ 278 struct AuditorAddRequest *prev; 279 280 /** 281 * Operation handle. 282 */ 283 struct TALER_EXCHANGE_ManagementAuditorEnableHandle *h; 284 285 /** 286 * Array index of the associated command. 287 */ 288 size_t idx; 289 }; 290 291 292 /** 293 * Data structure for auditor del requests. 294 */ 295 struct AuditorDelRequest 296 { 297 298 /** 299 * Kept in a DLL. 300 */ 301 struct AuditorDelRequest *next; 302 303 /** 304 * Kept in a DLL. 305 */ 306 struct AuditorDelRequest *prev; 307 308 /** 309 * Operation handle. 310 */ 311 struct TALER_EXCHANGE_ManagementAuditorDisableHandle *h; 312 313 /** 314 * Array index of the associated command. 315 */ 316 size_t idx; 317 }; 318 319 320 /** 321 * Data structure for wire add requests. 322 */ 323 struct WireAddRequest 324 { 325 326 /** 327 * Kept in a DLL. 328 */ 329 struct WireAddRequest *next; 330 331 /** 332 * Kept in a DLL. 333 */ 334 struct WireAddRequest *prev; 335 336 /** 337 * Operation handle. 338 */ 339 struct TALER_EXCHANGE_ManagementWireEnableHandle *h; 340 341 /** 342 * Array index of the associated command. 343 */ 344 size_t idx; 345 }; 346 347 348 /** 349 * Data structure for wire del requests. 350 */ 351 struct WireDelRequest 352 { 353 354 /** 355 * Kept in a DLL. 356 */ 357 struct WireDelRequest *next; 358 359 /** 360 * Kept in a DLL. 361 */ 362 struct WireDelRequest *prev; 363 364 /** 365 * Operation handle. 366 */ 367 struct TALER_EXCHANGE_ManagementWireDisableHandle *h; 368 369 /** 370 * Array index of the associated command. 371 */ 372 size_t idx; 373 }; 374 375 376 /** 377 * Data structure for announcing wire fees. 378 */ 379 struct WireFeeRequest 380 { 381 382 /** 383 * Kept in a DLL. 384 */ 385 struct WireFeeRequest *next; 386 387 /** 388 * Kept in a DLL. 389 */ 390 struct WireFeeRequest *prev; 391 392 /** 393 * Operation handle. 394 */ 395 struct TALER_EXCHANGE_ManagementSetWireFeeHandle *h; 396 397 /** 398 * Array index of the associated command. 399 */ 400 size_t idx; 401 }; 402 403 404 /** 405 * Data structure for draining profits. 406 */ 407 struct DrainProfitsRequest 408 { 409 410 /** 411 * Kept in a DLL. 412 */ 413 struct DrainProfitsRequest *next; 414 415 /** 416 * Kept in a DLL. 417 */ 418 struct DrainProfitsRequest *prev; 419 420 /** 421 * Operation handle. 422 */ 423 struct TALER_EXCHANGE_ManagementDrainProfitsHandle *h; 424 425 /** 426 * Array index of the associated command. 427 */ 428 size_t idx; 429 }; 430 431 432 /** 433 * Data structure for announcing global fees. 434 */ 435 struct GlobalFeeRequest 436 { 437 438 /** 439 * Kept in a DLL. 440 */ 441 struct GlobalFeeRequest *next; 442 443 /** 444 * Kept in a DLL. 445 */ 446 struct GlobalFeeRequest *prev; 447 448 /** 449 * Operation handle. 450 */ 451 struct TALER_EXCHANGE_ManagementSetGlobalFeeHandle *h; 452 453 /** 454 * Array index of the associated command. 455 */ 456 size_t idx; 457 }; 458 459 460 /** 461 * Ongoing /keys request. 462 */ 463 struct UploadKeysRequest 464 { 465 /** 466 * Kept in a DLL. 467 */ 468 struct UploadKeysRequest *next; 469 470 /** 471 * Kept in a DLL. 472 */ 473 struct UploadKeysRequest *prev; 474 475 /** 476 * Operation handle. 477 */ 478 struct TALER_EXCHANGE_ManagementPostKeysHandle *h; 479 480 /** 481 * Operation index. 482 */ 483 size_t idx; 484 }; 485 486 /** 487 * Ongoing /management/extensions request. 488 */ 489 struct UploadExtensionsRequest 490 { 491 /** 492 * Kept in a DLL. 493 */ 494 struct UploadExtensionsRequest *next; 495 496 /** 497 * Kept in a DLL. 498 */ 499 struct UploadExtensionsRequest *prev; 500 501 /** 502 * Operation handle. 503 */ 504 struct TALER_EXCHANGE_ManagementPostExtensionsHandle *h; 505 506 /** 507 * Operation index. 508 */ 509 size_t idx; 510 }; 511 512 513 /** 514 * Data structure for AML staff requests. 515 */ 516 struct AmlStaffRequest 517 { 518 519 /** 520 * Kept in a DLL. 521 */ 522 struct AmlStaffRequest *next; 523 524 /** 525 * Kept in a DLL. 526 */ 527 struct AmlStaffRequest *prev; 528 529 /** 530 * Operation handle. 531 */ 532 struct TALER_EXCHANGE_ManagementUpdateAmlOfficer *h; 533 534 /** 535 * Array index of the associated command. 536 */ 537 size_t idx; 538 }; 539 540 541 /** 542 * Data structure for partner add requests. 543 */ 544 struct PartnerAddRequest 545 { 546 547 /** 548 * Kept in a DLL. 549 */ 550 struct PartnerAddRequest *next; 551 552 /** 553 * Kept in a DLL. 554 */ 555 struct PartnerAddRequest *prev; 556 557 /** 558 * Operation handle. 559 */ 560 struct TALER_EXCHANGE_ManagementAddPartner *h; 561 562 /** 563 * Array index of the associated command. 564 */ 565 size_t idx; 566 }; 567 568 569 /** 570 * Next work item to perform. 571 */ 572 static struct GNUNET_SCHEDULER_Task *nxt; 573 574 /** 575 * Handle for #do_download. 576 */ 577 static struct TALER_EXCHANGE_ManagementGetKeysHandle *mgkh; 578 579 /** 580 * Active AML staff change requests. 581 */ 582 static struct AmlStaffRequest *asr_head; 583 584 /** 585 * Active AML staff change requests. 586 */ 587 static struct AmlStaffRequest *asr_tail; 588 589 /** 590 * Active partner add requests. 591 */ 592 static struct PartnerAddRequest *par_head; 593 594 /** 595 * Active partner add requests. 596 */ 597 static struct PartnerAddRequest *par_tail; 598 599 /** 600 * Active denomiantion revocation requests. 601 */ 602 static struct DenomRevocationRequest *drr_head; 603 604 /** 605 * Active denomiantion revocation requests. 606 */ 607 static struct DenomRevocationRequest *drr_tail; 608 609 /** 610 * Active signkey revocation requests. 611 */ 612 static struct SignkeyRevocationRequest *srr_head; 613 614 /** 615 * Active signkey revocation requests. 616 */ 617 static struct SignkeyRevocationRequest *srr_tail; 618 619 /** 620 * Active auditor add requests. 621 */ 622 static struct AuditorAddRequest *aar_head; 623 624 /** 625 * Active auditor add requests. 626 */ 627 static struct AuditorAddRequest *aar_tail; 628 629 /** 630 * Active auditor del requests. 631 */ 632 static struct AuditorDelRequest *adr_head; 633 634 /** 635 * Active auditor del requests. 636 */ 637 static struct AuditorDelRequest *adr_tail; 638 639 /** 640 * Active wire add requests. 641 */ 642 static struct WireAddRequest *war_head; 643 644 /** 645 * Active wire add requests. 646 */ 647 static struct WireAddRequest *war_tail; 648 649 /** 650 * Active wire del requests. 651 */ 652 static struct WireDelRequest *wdr_head; 653 654 /** 655 * Active wire del requests. 656 */ 657 static struct WireDelRequest *wdr_tail; 658 659 /** 660 * Active wire fee requests. 661 */ 662 static struct WireFeeRequest *wfr_head; 663 664 /** 665 * Active wire fee requests. 666 */ 667 static struct WireFeeRequest *wfr_tail; 668 669 /** 670 * Active global fee requests. 671 */ 672 static struct GlobalFeeRequest *gfr_head; 673 674 /** 675 * Active global fee requests. 676 */ 677 static struct GlobalFeeRequest *gfr_tail; 678 679 /** 680 * Active keys upload requests. 681 */ 682 static struct UploadKeysRequest *ukr_head; 683 684 /** 685 * Active keys upload requests. 686 */ 687 static struct UploadKeysRequest *ukr_tail; 688 689 /** 690 * Active extensions upload requests. 691 */ 692 static struct UploadExtensionsRequest *uer_head; 693 694 /** 695 * Active extensions upload requests. 696 */ 697 static struct UploadExtensionsRequest *uer_tail; 698 699 /** 700 * Active drain profits requests. 701 */ 702 struct DrainProfitsRequest *dpr_head; 703 704 /** 705 * Active drain profits requests. 706 */ 707 static struct DrainProfitsRequest *dpr_tail; 708 709 710 /** 711 * Shutdown task. Invoked when the application is being terminated. 712 * 713 * @param cls NULL 714 */ 715 static void 716 do_shutdown (void *cls) 717 { 718 (void) cls; 719 720 { 721 struct AmlStaffRequest *asr; 722 723 while (NULL != (asr = asr_head)) 724 { 725 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 726 "Aborting incomplete AML staff update #%u\n", 727 (unsigned int) asr->idx); 728 TALER_EXCHANGE_management_update_aml_officer_cancel (asr->h); 729 GNUNET_CONTAINER_DLL_remove (asr_head, 730 asr_tail, 731 asr); 732 GNUNET_free (asr); 733 } 734 } 735 { 736 struct PartnerAddRequest *par; 737 738 while (NULL != (par = par_head)) 739 { 740 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 741 "Aborting incomplete partner add request #%u\n", 742 (unsigned int) par->idx); 743 TALER_EXCHANGE_management_add_partner_cancel (par->h); 744 GNUNET_CONTAINER_DLL_remove (par_head, 745 par_tail, 746 par); 747 GNUNET_free (par); 748 } 749 } 750 { 751 struct DenomRevocationRequest *drr; 752 753 while (NULL != (drr = drr_head)) 754 { 755 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 756 "Aborting incomplete denomination revocation #%u\n", 757 (unsigned int) drr->idx); 758 TALER_EXCHANGE_management_revoke_denomination_key_cancel (drr->h); 759 GNUNET_CONTAINER_DLL_remove (drr_head, 760 drr_tail, 761 drr); 762 GNUNET_free (drr); 763 } 764 } 765 { 766 struct SignkeyRevocationRequest *srr; 767 768 while (NULL != (srr = srr_head)) 769 { 770 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 771 "Aborting incomplete signkey revocation #%u\n", 772 (unsigned int) srr->idx); 773 TALER_EXCHANGE_management_revoke_signing_key_cancel (srr->h); 774 GNUNET_CONTAINER_DLL_remove (srr_head, 775 srr_tail, 776 srr); 777 GNUNET_free (srr); 778 } 779 } 780 781 { 782 struct AuditorAddRequest *aar; 783 784 while (NULL != (aar = aar_head)) 785 { 786 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 787 "Aborting incomplete auditor add #%u\n", 788 (unsigned int) aar->idx); 789 TALER_EXCHANGE_management_enable_auditor_cancel (aar->h); 790 GNUNET_CONTAINER_DLL_remove (aar_head, 791 aar_tail, 792 aar); 793 GNUNET_free (aar); 794 } 795 } 796 { 797 struct AuditorDelRequest *adr; 798 799 while (NULL != (adr = adr_head)) 800 { 801 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 802 "Aborting incomplete auditor del #%u\n", 803 (unsigned int) adr->idx); 804 TALER_EXCHANGE_management_disable_auditor_cancel (adr->h); 805 GNUNET_CONTAINER_DLL_remove (adr_head, 806 adr_tail, 807 adr); 808 GNUNET_free (adr); 809 } 810 } 811 { 812 struct WireAddRequest *war; 813 814 while (NULL != (war = war_head)) 815 { 816 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 817 "Aborting incomplete wire add #%u\n", 818 (unsigned int) war->idx); 819 TALER_EXCHANGE_management_enable_wire_cancel (war->h); 820 GNUNET_CONTAINER_DLL_remove (war_head, 821 war_tail, 822 war); 823 GNUNET_free (war); 824 } 825 } 826 { 827 struct WireDelRequest *wdr; 828 829 while (NULL != (wdr = wdr_head)) 830 { 831 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 832 "Aborting incomplete wire del #%u\n", 833 (unsigned int) wdr->idx); 834 TALER_EXCHANGE_management_disable_wire_cancel (wdr->h); 835 GNUNET_CONTAINER_DLL_remove (wdr_head, 836 wdr_tail, 837 wdr); 838 GNUNET_free (wdr); 839 } 840 } 841 { 842 struct WireFeeRequest *wfr; 843 844 while (NULL != (wfr = wfr_head)) 845 { 846 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 847 "Aborting incomplete wire fee #%u\n", 848 (unsigned int) wfr->idx); 849 TALER_EXCHANGE_management_set_wire_fees_cancel (wfr->h); 850 GNUNET_CONTAINER_DLL_remove (wfr_head, 851 wfr_tail, 852 wfr); 853 GNUNET_free (wfr); 854 } 855 } 856 { 857 struct GlobalFeeRequest *gfr; 858 859 while (NULL != (gfr = gfr_head)) 860 { 861 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 862 "Aborting incomplete global fee #%u\n", 863 (unsigned int) gfr->idx); 864 TALER_EXCHANGE_management_set_global_fees_cancel (gfr->h); 865 GNUNET_CONTAINER_DLL_remove (gfr_head, 866 gfr_tail, 867 gfr); 868 GNUNET_free (gfr); 869 } 870 } 871 { 872 struct UploadKeysRequest *ukr; 873 874 while (NULL != (ukr = ukr_head)) 875 { 876 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 877 "Aborting incomplete key signature upload #%u\n", 878 (unsigned int) ukr->idx); 879 TALER_EXCHANGE_post_management_keys_cancel (ukr->h); 880 GNUNET_CONTAINER_DLL_remove (ukr_head, 881 ukr_tail, 882 ukr); 883 GNUNET_free (ukr); 884 } 885 } 886 { 887 struct UploadExtensionsRequest *uer; 888 889 while (NULL != (uer = uer_head)) 890 { 891 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 892 "Aborting incomplete extensions signature upload #%u\n", 893 (unsigned int) uer->idx); 894 TALER_EXCHANGE_management_post_extensions_cancel (uer->h); 895 GNUNET_CONTAINER_DLL_remove (uer_head, 896 uer_tail, 897 uer); 898 GNUNET_free (uer); 899 } 900 } 901 902 { 903 struct DrainProfitsRequest *dpr; 904 905 while (NULL != (dpr = dpr_head)) 906 { 907 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 908 "Aborting incomplete drain profits request #%u\n", 909 (unsigned int) dpr->idx); 910 TALER_EXCHANGE_management_drain_profits_cancel (dpr->h); 911 GNUNET_CONTAINER_DLL_remove (dpr_head, 912 dpr_tail, 913 dpr); 914 GNUNET_free (dpr); 915 } 916 } 917 918 if (NULL != out) 919 { 920 if (EXIT_SUCCESS == global_ret) 921 json_dumpf (out, 922 stdout, 923 JSON_INDENT (2)); 924 json_decref (out); 925 out = NULL; 926 } 927 if (NULL != in) 928 { 929 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 930 "Input not consumed!\n"); 931 json_decref (in); 932 in = NULL; 933 } 934 if (NULL != nxt) 935 { 936 GNUNET_SCHEDULER_cancel (nxt); 937 nxt = NULL; 938 } 939 if (NULL != mgkh) 940 { 941 TALER_EXCHANGE_get_management_keys_cancel (mgkh); 942 mgkh = NULL; 943 } 944 if (NULL != ctx) 945 { 946 GNUNET_CURL_fini (ctx); 947 ctx = NULL; 948 } 949 if (NULL != rc) 950 { 951 GNUNET_CURL_gnunet_rc_destroy (rc); 952 rc = NULL; 953 } 954 } 955 956 957 /** 958 * Test if we should shut down because all tasks are done. 959 */ 960 static void 961 test_shutdown (void) 962 { 963 if ( (NULL == drr_head) && 964 (NULL == par_head) && 965 (NULL == asr_head) && 966 (NULL == srr_head) && 967 (NULL == aar_head) && 968 (NULL == adr_head) && 969 (NULL == war_head) && 970 (NULL == wdr_head) && 971 (NULL == wfr_head) && 972 (NULL == gfr_head) && 973 (NULL == ukr_head) && 974 (NULL == uer_head) && 975 (NULL == dpr_head) && 976 (NULL == mgkh) && 977 (NULL == nxt) ) 978 GNUNET_SCHEDULER_shutdown (); 979 } 980 981 982 /** 983 * Function to continue processing the next command. 984 * 985 * @param cls must be a `char *const*` with the array of 986 * command-line arguments to process next 987 */ 988 static void 989 work (void *cls); 990 991 992 /** 993 * Function to schedule job to process the next command. 994 * 995 * @param args the array of command-line arguments to process next 996 */ 997 static void 998 next (char *const *args) 999 { 1000 GNUNET_assert (NULL == nxt); 1001 if (NULL == args[0]) 1002 { 1003 test_shutdown (); 1004 return; 1005 } 1006 nxt = GNUNET_SCHEDULER_add_now (&work, 1007 (void *) args); 1008 } 1009 1010 1011 /** 1012 * Add an operation to the #out JSON array for processing later. 1013 * 1014 * @param op_name name of the operation 1015 * @param op_value values for the operation (consumed) 1016 */ 1017 static void 1018 output_operation (const char *op_name, 1019 json_t *op_value) 1020 { 1021 json_t *action; 1022 1023 GNUNET_break (NULL != op_value); 1024 if (NULL == out) 1025 { 1026 out = json_array (); 1027 GNUNET_assert (NULL != out); 1028 } 1029 action = GNUNET_JSON_PACK ( 1030 GNUNET_JSON_pack_string ("operation", 1031 op_name), 1032 GNUNET_JSON_pack_object_steal ("arguments", 1033 op_value)); 1034 GNUNET_assert (0 == 1035 json_array_append_new (out, 1036 action)); 1037 } 1038 1039 1040 /** 1041 * Information about a subroutine for an upload. 1042 */ 1043 struct UploadHandler 1044 { 1045 /** 1046 * Key to trigger this subroutine. 1047 */ 1048 const char *key; 1049 1050 /** 1051 * Function implementing an upload. 1052 * 1053 * @param exchange_url URL of the exchange 1054 * @param idx index of the operation we are performing 1055 * @param value arguments to drive the upload. 1056 */ 1057 void (*cb)(const char *exchange_url, 1058 size_t idx, 1059 const json_t *value); 1060 1061 }; 1062 1063 1064 /** 1065 * Load the offline key (if not yet done). Triggers shutdown on failure. 1066 * 1067 * @param do_create #GNUNET_YES if the key may be created 1068 * @return #GNUNET_OK on success 1069 */ 1070 static enum GNUNET_GenericReturnValue 1071 load_offline_key (int do_create) 1072 { 1073 static bool done; 1074 int ret; 1075 char *fn; 1076 1077 if (done) 1078 return GNUNET_OK; 1079 if (GNUNET_OK != 1080 GNUNET_CONFIGURATION_get_value_filename (kcfg, 1081 "exchange-offline", 1082 "MASTER_PRIV_FILE", 1083 &fn)) 1084 { 1085 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 1086 "exchange-offline", 1087 "MASTER_PRIV_FILE"); 1088 test_shutdown (); 1089 return GNUNET_SYSERR; 1090 } 1091 if (GNUNET_YES != 1092 GNUNET_DISK_file_test (fn)) 1093 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1094 "Exchange master private key `%s' does not exist yet, creating it!\n", 1095 fn); 1096 ret = GNUNET_CRYPTO_eddsa_key_from_file (fn, 1097 do_create, 1098 &master_priv.eddsa_priv); 1099 if (GNUNET_SYSERR == ret) 1100 { 1101 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1102 "Failed to initialize master key from file `%s': %s\n", 1103 fn, 1104 "could not create file"); 1105 GNUNET_free (fn); 1106 test_shutdown (); 1107 return GNUNET_SYSERR; 1108 } 1109 GNUNET_free (fn); 1110 GNUNET_CRYPTO_eddsa_key_get_public (&master_priv.eddsa_priv, 1111 &master_pub.eddsa_pub); 1112 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1113 "Using master public key %s\n", 1114 TALER_B2S (&master_pub)); 1115 done = true; 1116 return GNUNET_OK; 1117 } 1118 1119 1120 /** 1121 * Function called with information about the post revocation operation result. 1122 * 1123 * @param cls closure with a `struct DenomRevocationRequest` 1124 * @param dr response data 1125 */ 1126 static void 1127 denom_revocation_cb ( 1128 void *cls, 1129 const struct TALER_EXCHANGE_ManagementRevokeDenominationResponse *dr) 1130 { 1131 struct DenomRevocationRequest *drr = cls; 1132 const struct TALER_EXCHANGE_HttpResponse *hr = &dr->hr; 1133 1134 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1135 { 1136 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1137 "Upload failed for command %u with status %u: %s (%s)\n", 1138 (unsigned int) drr->idx, 1139 hr->http_status, 1140 hr->hint, 1141 TALER_JSON_get_error_hint (hr->reply)); 1142 global_ret = EXIT_FAILURE; 1143 } 1144 GNUNET_CONTAINER_DLL_remove (drr_head, 1145 drr_tail, 1146 drr); 1147 GNUNET_free (drr); 1148 test_shutdown (); 1149 } 1150 1151 1152 /** 1153 * Upload denomination revocation request data. 1154 * 1155 * @param exchange_url base URL of the exchange 1156 * @param idx index of the operation we are performing (for logging) 1157 * @param value arguments for denomination revocation 1158 */ 1159 static void 1160 upload_denom_revocation (const char *exchange_url, 1161 size_t idx, 1162 const json_t *value) 1163 { 1164 struct TALER_MasterSignatureP master_sig; 1165 struct TALER_DenominationHashP h_denom_pub; 1166 struct DenomRevocationRequest *drr; 1167 const char *err_name; 1168 unsigned int err_line; 1169 struct GNUNET_JSON_Specification spec[] = { 1170 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 1171 &h_denom_pub), 1172 GNUNET_JSON_spec_fixed_auto ("master_sig", 1173 &master_sig), 1174 GNUNET_JSON_spec_end () 1175 }; 1176 1177 if (GNUNET_OK != 1178 GNUNET_JSON_parse (value, 1179 spec, 1180 &err_name, 1181 &err_line)) 1182 { 1183 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1184 "Invalid input for denomination revocation: %s#%u at %u (skipping)\n", 1185 err_name, 1186 err_line, 1187 (unsigned int) idx); 1188 json_dumpf (value, 1189 stderr, 1190 JSON_INDENT (2)); 1191 global_ret = EXIT_FAILURE; 1192 GNUNET_SCHEDULER_shutdown (); 1193 return; 1194 } 1195 drr = GNUNET_new (struct DenomRevocationRequest); 1196 drr->idx = idx; 1197 drr->h = 1198 TALER_EXCHANGE_management_revoke_denomination_key (ctx, 1199 exchange_url, 1200 &h_denom_pub, 1201 &master_sig, 1202 &denom_revocation_cb, 1203 drr); 1204 GNUNET_CONTAINER_DLL_insert (drr_head, 1205 drr_tail, 1206 drr); 1207 } 1208 1209 1210 /** 1211 * Function called with information about the post revocation operation result. 1212 * 1213 * @param cls closure with a `struct SignkeyRevocationRequest` 1214 * @param sr response data 1215 */ 1216 static void 1217 signkey_revocation_cb ( 1218 void *cls, 1219 const struct TALER_EXCHANGE_ManagementRevokeSigningKeyResponse *sr) 1220 { 1221 struct SignkeyRevocationRequest *srr = cls; 1222 const struct TALER_EXCHANGE_HttpResponse *hr = &sr->hr; 1223 1224 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1225 { 1226 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1227 "Upload failed for command %u with status %u: %s (%s)\n", 1228 (unsigned int) srr->idx, 1229 hr->http_status, 1230 hr->hint, 1231 TALER_JSON_get_error_hint (hr->reply)); 1232 global_ret = EXIT_FAILURE; 1233 } 1234 GNUNET_CONTAINER_DLL_remove (srr_head, 1235 srr_tail, 1236 srr); 1237 GNUNET_free (srr); 1238 test_shutdown (); 1239 } 1240 1241 1242 /** 1243 * Upload signkey revocation request data. 1244 * 1245 * @param exchange_url base URL of the exchange 1246 * @param idx index of the operation we are performing (for logging) 1247 * @param value arguments for denomination revocation 1248 */ 1249 static void 1250 upload_signkey_revocation (const char *exchange_url, 1251 size_t idx, 1252 const json_t *value) 1253 { 1254 struct TALER_MasterSignatureP master_sig; 1255 struct TALER_ExchangePublicKeyP exchange_pub; 1256 struct SignkeyRevocationRequest *srr; 1257 const char *err_name; 1258 unsigned int err_line; 1259 struct GNUNET_JSON_Specification spec[] = { 1260 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 1261 &exchange_pub), 1262 GNUNET_JSON_spec_fixed_auto ("master_sig", 1263 &master_sig), 1264 GNUNET_JSON_spec_end () 1265 }; 1266 1267 if (GNUNET_OK != 1268 GNUNET_JSON_parse (value, 1269 spec, 1270 &err_name, 1271 &err_line)) 1272 { 1273 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1274 "Invalid input for signkey revocation: %s#%u at %u (skipping)\n", 1275 err_name, 1276 err_line, 1277 (unsigned int) idx); 1278 json_dumpf (value, 1279 stderr, 1280 JSON_INDENT (2)); 1281 global_ret = EXIT_FAILURE; 1282 GNUNET_SCHEDULER_shutdown (); 1283 return; 1284 } 1285 srr = GNUNET_new (struct SignkeyRevocationRequest); 1286 srr->idx = idx; 1287 srr->h = 1288 TALER_EXCHANGE_management_revoke_signing_key (ctx, 1289 exchange_url, 1290 &exchange_pub, 1291 &master_sig, 1292 &signkey_revocation_cb, 1293 srr); 1294 GNUNET_CONTAINER_DLL_insert (srr_head, 1295 srr_tail, 1296 srr); 1297 } 1298 1299 1300 /** 1301 * Function called with information about the post auditor add operation result. 1302 * 1303 * @param cls closure with a `struct AuditorAddRequest` 1304 * @param mer response data 1305 */ 1306 static void 1307 auditor_add_cb ( 1308 void *cls, 1309 const struct TALER_EXCHANGE_ManagementAuditorEnableResponse *mer) 1310 { 1311 struct AuditorAddRequest *aar = cls; 1312 const struct TALER_EXCHANGE_HttpResponse *hr = &mer->hr; 1313 1314 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1315 { 1316 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1317 "Upload failed for command %u with status %u: %s (%s)\n", 1318 (unsigned int) aar->idx, 1319 hr->http_status, 1320 TALER_ErrorCode_get_hint (hr->ec), 1321 hr->hint); 1322 global_ret = EXIT_FAILURE; 1323 } 1324 GNUNET_CONTAINER_DLL_remove (aar_head, 1325 aar_tail, 1326 aar); 1327 GNUNET_free (aar); 1328 test_shutdown (); 1329 } 1330 1331 1332 /** 1333 * Upload auditor add data. 1334 * 1335 * @param exchange_url base URL of the exchange 1336 * @param idx index of the operation we are performing (for logging) 1337 * @param value arguments for denomination revocation 1338 */ 1339 static void 1340 upload_auditor_add (const char *exchange_url, 1341 size_t idx, 1342 const json_t *value) 1343 { 1344 struct TALER_MasterSignatureP master_sig; 1345 const char *auditor_url; 1346 const char *auditor_name; 1347 struct GNUNET_TIME_Timestamp start_time; 1348 struct TALER_AuditorPublicKeyP auditor_pub; 1349 struct AuditorAddRequest *aar; 1350 const char *err_name; 1351 unsigned int err_line; 1352 struct GNUNET_JSON_Specification spec[] = { 1353 TALER_JSON_spec_web_url ("auditor_url", 1354 &auditor_url), 1355 GNUNET_JSON_spec_string ("auditor_name", 1356 &auditor_name), 1357 GNUNET_JSON_spec_timestamp ("validity_start", 1358 &start_time), 1359 GNUNET_JSON_spec_fixed_auto ("auditor_pub", 1360 &auditor_pub), 1361 GNUNET_JSON_spec_fixed_auto ("master_sig", 1362 &master_sig), 1363 GNUNET_JSON_spec_end () 1364 }; 1365 1366 if (GNUNET_OK != 1367 GNUNET_JSON_parse (value, 1368 spec, 1369 &err_name, 1370 &err_line)) 1371 { 1372 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1373 "Invalid input for adding auditor: %s#%u at %u (skipping)\n", 1374 err_name, 1375 err_line, 1376 (unsigned int) idx); 1377 json_dumpf (value, 1378 stderr, 1379 JSON_INDENT (2)); 1380 global_ret = EXIT_FAILURE; 1381 GNUNET_SCHEDULER_shutdown (); 1382 return; 1383 } 1384 aar = GNUNET_new (struct AuditorAddRequest); 1385 aar->idx = idx; 1386 aar->h = 1387 TALER_EXCHANGE_management_enable_auditor (ctx, 1388 exchange_url, 1389 &auditor_pub, 1390 auditor_url, 1391 auditor_name, 1392 start_time, 1393 &master_sig, 1394 &auditor_add_cb, 1395 aar); 1396 GNUNET_CONTAINER_DLL_insert (aar_head, 1397 aar_tail, 1398 aar); 1399 } 1400 1401 1402 /** 1403 * Function called with information about the post auditor del operation result. 1404 * 1405 * @param cls closure with a `struct AuditorDelRequest` 1406 * @param mdr response data 1407 */ 1408 static void 1409 auditor_del_cb (void *cls, 1410 const struct 1411 TALER_EXCHANGE_ManagementAuditorDisableResponse *mdr) 1412 { 1413 struct AuditorDelRequest *adr = cls; 1414 const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; 1415 1416 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1417 { 1418 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1419 "Upload failed for command %u with status %u: %s (%s)\n", 1420 (unsigned int) adr->idx, 1421 hr->http_status, 1422 TALER_ErrorCode_get_hint (hr->ec), 1423 hr->hint); 1424 global_ret = EXIT_FAILURE; 1425 } 1426 GNUNET_CONTAINER_DLL_remove (adr_head, 1427 adr_tail, 1428 adr); 1429 GNUNET_free (adr); 1430 test_shutdown (); 1431 } 1432 1433 1434 /** 1435 * Upload auditor del data. 1436 * 1437 * @param exchange_url base URL of the exchange 1438 * @param idx index of the operation we are performing (for logging) 1439 * @param value arguments for denomination revocation 1440 */ 1441 static void 1442 upload_auditor_del (const char *exchange_url, 1443 size_t idx, 1444 const json_t *value) 1445 { 1446 struct TALER_AuditorPublicKeyP auditor_pub; 1447 struct TALER_MasterSignatureP master_sig; 1448 struct GNUNET_TIME_Timestamp end_time; 1449 struct AuditorDelRequest *adr; 1450 const char *err_name; 1451 unsigned int err_line; 1452 struct GNUNET_JSON_Specification spec[] = { 1453 GNUNET_JSON_spec_fixed_auto ("auditor_pub", 1454 &auditor_pub), 1455 GNUNET_JSON_spec_timestamp ("validity_end", 1456 &end_time), 1457 GNUNET_JSON_spec_fixed_auto ("master_sig", 1458 &master_sig), 1459 GNUNET_JSON_spec_end () 1460 }; 1461 1462 if (GNUNET_OK != 1463 GNUNET_JSON_parse (value, 1464 spec, 1465 &err_name, 1466 &err_line)) 1467 { 1468 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1469 "Invalid input to disable auditor: %s#%u at %u (skipping)\n", 1470 err_name, 1471 err_line, 1472 (unsigned int) idx); 1473 json_dumpf (value, 1474 stderr, 1475 JSON_INDENT (2)); 1476 global_ret = EXIT_FAILURE; 1477 GNUNET_SCHEDULER_shutdown (); 1478 return; 1479 } 1480 adr = GNUNET_new (struct AuditorDelRequest); 1481 adr->idx = idx; 1482 adr->h = 1483 TALER_EXCHANGE_management_disable_auditor (ctx, 1484 exchange_url, 1485 &auditor_pub, 1486 end_time, 1487 &master_sig, 1488 &auditor_del_cb, 1489 adr); 1490 GNUNET_CONTAINER_DLL_insert (adr_head, 1491 adr_tail, 1492 adr); 1493 } 1494 1495 1496 /** 1497 * Function called with information about the post wire add operation result. 1498 * 1499 * @param cls closure with a `struct WireAddRequest` 1500 * @param wer response data 1501 */ 1502 static void 1503 wire_add_cb (void *cls, 1504 const struct TALER_EXCHANGE_ManagementWireEnableResponse *wer) 1505 { 1506 struct WireAddRequest *war = cls; 1507 const struct TALER_EXCHANGE_HttpResponse *hr = &wer->hr; 1508 1509 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1510 { 1511 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1512 "Upload failed for command %u with status %u: %s (%s)\n", 1513 (unsigned int) war->idx, 1514 hr->http_status, 1515 TALER_ErrorCode_get_hint (hr->ec), 1516 hr->hint); 1517 global_ret = EXIT_FAILURE; 1518 } 1519 GNUNET_CONTAINER_DLL_remove (war_head, 1520 war_tail, 1521 war); 1522 GNUNET_free (war); 1523 test_shutdown (); 1524 } 1525 1526 1527 /** 1528 * Upload wire add data. 1529 * 1530 * @param exchange_url base URL of the exchange 1531 * @param idx index of the operation we are performing (for logging) 1532 * @param value arguments for denomination revocation 1533 */ 1534 static void 1535 upload_wire_add (const char *exchange_url, 1536 size_t idx, 1537 const json_t *value) 1538 { 1539 struct TALER_MasterSignatureP master_sig_add; 1540 struct TALER_MasterSignatureP master_sig_wire; 1541 struct TALER_FullPayto payto_uri; 1542 struct GNUNET_TIME_Timestamp start_time; 1543 struct WireAddRequest *war; 1544 const char *err_name; 1545 const char *conversion_url = NULL; 1546 const char *bank_label = NULL; 1547 int64_t priority = 0; 1548 const json_t *debit_restrictions; 1549 const json_t *credit_restrictions; 1550 unsigned int err_line; 1551 struct GNUNET_JSON_Specification spec[] = { 1552 TALER_JSON_spec_full_payto_uri ("payto_uri", 1553 &payto_uri), 1554 GNUNET_JSON_spec_mark_optional ( 1555 TALER_JSON_spec_web_url ("conversion_url", 1556 &conversion_url), 1557 NULL), 1558 GNUNET_JSON_spec_mark_optional ( 1559 GNUNET_JSON_spec_string ("bank_label", 1560 &bank_label), 1561 NULL), 1562 GNUNET_JSON_spec_int64 ("priority", 1563 &priority), 1564 GNUNET_JSON_spec_array_const ("debit_restrictions", 1565 &debit_restrictions), 1566 GNUNET_JSON_spec_array_const ("credit_restrictions", 1567 &credit_restrictions), 1568 GNUNET_JSON_spec_timestamp ("validity_start", 1569 &start_time), 1570 GNUNET_JSON_spec_fixed_auto ("master_sig_add", 1571 &master_sig_add), 1572 GNUNET_JSON_spec_fixed_auto ("master_sig_wire", 1573 &master_sig_wire), 1574 GNUNET_JSON_spec_end () 1575 }; 1576 1577 if (GNUNET_OK != 1578 GNUNET_JSON_parse (value, 1579 spec, 1580 &err_name, 1581 &err_line)) 1582 { 1583 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1584 "Invalid input for adding wire account: %s#%u at %u (skipping)\n", 1585 err_name, 1586 err_line, 1587 (unsigned int) idx); 1588 json_dumpf (value, 1589 stderr, 1590 JSON_INDENT (2)); 1591 global_ret = EXIT_FAILURE; 1592 GNUNET_SCHEDULER_shutdown (); 1593 return; 1594 } 1595 { 1596 char *wire_method; 1597 1598 wire_method = TALER_payto_get_method (payto_uri.full_payto); 1599 if (NULL == wire_method) 1600 { 1601 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1602 "payto:// URI `%s' is malformed\n", 1603 payto_uri.full_payto); 1604 global_ret = EXIT_FAILURE; 1605 GNUNET_SCHEDULER_shutdown (); 1606 return; 1607 } 1608 GNUNET_free (wire_method); 1609 } 1610 { 1611 char *msg = TALER_payto_validate (payto_uri); 1612 1613 if (NULL != msg) 1614 { 1615 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1616 "payto URI is malformed: %s\n", 1617 msg); 1618 GNUNET_free (msg); 1619 GNUNET_SCHEDULER_shutdown (); 1620 global_ret = EXIT_INVALIDARGUMENT; 1621 return; 1622 } 1623 } 1624 war = GNUNET_new (struct WireAddRequest); 1625 war->idx = idx; 1626 war->h = 1627 TALER_EXCHANGE_management_enable_wire (ctx, 1628 exchange_url, 1629 payto_uri, 1630 conversion_url, 1631 debit_restrictions, 1632 credit_restrictions, 1633 start_time, 1634 &master_sig_add, 1635 &master_sig_wire, 1636 bank_label, 1637 priority, 1638 &wire_add_cb, 1639 war); 1640 GNUNET_CONTAINER_DLL_insert (war_head, 1641 war_tail, 1642 war); 1643 } 1644 1645 1646 /** 1647 * Function called with information about the post wire del operation result. 1648 * 1649 * @param cls closure with a `struct WireDelRequest` 1650 * @param wdres response data 1651 */ 1652 static void 1653 wire_del_cb (void *cls, 1654 const struct TALER_EXCHANGE_ManagementWireDisableResponse *wdres) 1655 { 1656 struct WireDelRequest *wdr = cls; 1657 const struct TALER_EXCHANGE_HttpResponse *hr = &wdres->hr; 1658 1659 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1660 { 1661 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1662 "Upload failed for command %u with status %u: %s (%s)\n", 1663 (unsigned int) wdr->idx, 1664 hr->http_status, 1665 TALER_ErrorCode_get_hint (hr->ec), 1666 hr->hint); 1667 global_ret = EXIT_FAILURE; 1668 } 1669 GNUNET_CONTAINER_DLL_remove (wdr_head, 1670 wdr_tail, 1671 wdr); 1672 GNUNET_free (wdr); 1673 test_shutdown (); 1674 } 1675 1676 1677 /** 1678 * Upload wire del data. 1679 * 1680 * @param exchange_url base URL of the exchange 1681 * @param idx index of the operation we are performing (for logging) 1682 * @param value arguments for denomination revocation 1683 */ 1684 static void 1685 upload_wire_del (const char *exchange_url, 1686 size_t idx, 1687 const json_t *value) 1688 { 1689 struct TALER_MasterSignatureP master_sig; 1690 struct TALER_FullPayto payto_uri; 1691 struct GNUNET_TIME_Timestamp end_time; 1692 struct WireDelRequest *wdr; 1693 const char *err_name; 1694 unsigned int err_line; 1695 struct GNUNET_JSON_Specification spec[] = { 1696 TALER_JSON_spec_full_payto_uri ("payto_uri", 1697 &payto_uri), 1698 GNUNET_JSON_spec_timestamp ("validity_end", 1699 &end_time), 1700 GNUNET_JSON_spec_fixed_auto ("master_sig", 1701 &master_sig), 1702 GNUNET_JSON_spec_end () 1703 }; 1704 1705 if (GNUNET_OK != 1706 GNUNET_JSON_parse (value, 1707 spec, 1708 &err_name, 1709 &err_line)) 1710 { 1711 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1712 "Invalid input to disable wire account: %s#%u at %u (skipping)\n", 1713 err_name, 1714 err_line, 1715 (unsigned int) idx); 1716 json_dumpf (value, 1717 stderr, 1718 JSON_INDENT (2)); 1719 global_ret = EXIT_FAILURE; 1720 GNUNET_SCHEDULER_shutdown (); 1721 return; 1722 } 1723 wdr = GNUNET_new (struct WireDelRequest); 1724 wdr->idx = idx; 1725 wdr->h = 1726 TALER_EXCHANGE_management_disable_wire (ctx, 1727 exchange_url, 1728 payto_uri, 1729 end_time, 1730 &master_sig, 1731 &wire_del_cb, 1732 wdr); 1733 GNUNET_CONTAINER_DLL_insert (wdr_head, 1734 wdr_tail, 1735 wdr); 1736 } 1737 1738 1739 /** 1740 * Function called with information about the post wire fee operation result. 1741 * 1742 * @param cls closure with a `struct WireFeeRequest` 1743 * @param swr response data 1744 */ 1745 static void 1746 wire_fee_cb ( 1747 void *cls, 1748 const struct TALER_EXCHANGE_ManagementSetWireFeeResponse *swr) 1749 { 1750 struct WireFeeRequest *wfr = cls; 1751 const struct TALER_EXCHANGE_HttpResponse *hr = &swr->hr; 1752 1753 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1754 { 1755 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1756 "Upload failed for command %u with status %u: %s (%s)\n", 1757 (unsigned int) wfr->idx, 1758 hr->http_status, 1759 TALER_ErrorCode_get_hint (hr->ec), 1760 hr->hint); 1761 global_ret = EXIT_FAILURE; 1762 } 1763 GNUNET_CONTAINER_DLL_remove (wfr_head, 1764 wfr_tail, 1765 wfr); 1766 GNUNET_free (wfr); 1767 test_shutdown (); 1768 } 1769 1770 1771 /** 1772 * Upload wire fee. 1773 * 1774 * @param exchange_url base URL of the exchange 1775 * @param idx index of the operation we are performing (for logging) 1776 * @param value arguments for denomination revocation 1777 */ 1778 static void 1779 upload_wire_fee (const char *exchange_url, 1780 size_t idx, 1781 const json_t *value) 1782 { 1783 struct TALER_MasterSignatureP master_sig; 1784 const char *wire_method; 1785 struct WireFeeRequest *wfr; 1786 const char *err_name; 1787 unsigned int err_line; 1788 struct TALER_WireFeeSet fees; 1789 struct GNUNET_TIME_Timestamp start_time; 1790 struct GNUNET_TIME_Timestamp end_time; 1791 struct GNUNET_JSON_Specification spec[] = { 1792 GNUNET_JSON_spec_string ("wire_method", 1793 &wire_method), 1794 TALER_JSON_spec_amount ("wire_fee", 1795 currency, 1796 &fees.wire), 1797 TALER_JSON_spec_amount ("closing_fee", 1798 currency, 1799 &fees.closing), 1800 GNUNET_JSON_spec_timestamp ("start_time", 1801 &start_time), 1802 GNUNET_JSON_spec_timestamp ("end_time", 1803 &end_time), 1804 GNUNET_JSON_spec_fixed_auto ("master_sig", 1805 &master_sig), 1806 GNUNET_JSON_spec_end () 1807 }; 1808 1809 if (GNUNET_OK != 1810 GNUNET_JSON_parse (value, 1811 spec, 1812 &err_name, 1813 &err_line)) 1814 { 1815 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1816 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 1817 err_name, 1818 err_line, 1819 (unsigned int) idx); 1820 json_dumpf (value, 1821 stderr, 1822 JSON_INDENT (2)); 1823 global_ret = EXIT_FAILURE; 1824 GNUNET_SCHEDULER_shutdown (); 1825 return; 1826 } 1827 wfr = GNUNET_new (struct WireFeeRequest); 1828 wfr->idx = idx; 1829 wfr->h = 1830 TALER_EXCHANGE_management_set_wire_fees (ctx, 1831 exchange_url, 1832 wire_method, 1833 start_time, 1834 end_time, 1835 &fees, 1836 &master_sig, 1837 &wire_fee_cb, 1838 wfr); 1839 GNUNET_CONTAINER_DLL_insert (wfr_head, 1840 wfr_tail, 1841 wfr); 1842 } 1843 1844 1845 /** 1846 * Function called with information about the post global fee operation result. 1847 * 1848 * @param cls closure with a `struct WireFeeRequest` 1849 * @param gr response data 1850 */ 1851 static void 1852 global_fee_cb ( 1853 void *cls, 1854 const struct TALER_EXCHANGE_ManagementSetGlobalFeeResponse *gr) 1855 { 1856 struct GlobalFeeRequest *gfr = cls; 1857 const struct TALER_EXCHANGE_HttpResponse *hr = &gr->hr; 1858 1859 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1860 { 1861 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1862 "Upload failed for command %u with status %u: %s (%s)\n", 1863 (unsigned int) gfr->idx, 1864 hr->http_status, 1865 TALER_ErrorCode_get_hint (hr->ec), 1866 hr->hint); 1867 global_ret = EXIT_FAILURE; 1868 } 1869 GNUNET_CONTAINER_DLL_remove (gfr_head, 1870 gfr_tail, 1871 gfr); 1872 GNUNET_free (gfr); 1873 test_shutdown (); 1874 } 1875 1876 1877 /** 1878 * Upload global fee. 1879 * 1880 * @param exchange_url base URL of the exchange 1881 * @param idx index of the operation we are performing (for logging) 1882 * @param value arguments for denomination revocation 1883 */ 1884 static void 1885 upload_global_fee (const char *exchange_url, 1886 size_t idx, 1887 const json_t *value) 1888 { 1889 struct TALER_MasterSignatureP master_sig; 1890 struct GlobalFeeRequest *gfr; 1891 const char *err_name; 1892 unsigned int err_line; 1893 struct TALER_GlobalFeeSet fees; 1894 struct GNUNET_TIME_Timestamp start_time; 1895 struct GNUNET_TIME_Timestamp end_time; 1896 struct GNUNET_TIME_Relative purse_timeout; 1897 struct GNUNET_TIME_Relative history_expiration; 1898 uint32_t purse_account_limit; 1899 struct GNUNET_JSON_Specification spec[] = { 1900 TALER_JSON_spec_amount ("history_fee", 1901 currency, 1902 &fees.history), 1903 TALER_JSON_spec_amount ("account_fee", 1904 currency, 1905 &fees.account), 1906 TALER_JSON_spec_amount ("purse_fee", 1907 currency, 1908 &fees.purse), 1909 GNUNET_JSON_spec_relative_time ("purse_timeout", 1910 &purse_timeout), 1911 GNUNET_JSON_spec_relative_time ("history_expiration", 1912 &history_expiration), 1913 GNUNET_JSON_spec_uint32 ("purse_account_limit", 1914 &purse_account_limit), 1915 GNUNET_JSON_spec_timestamp ("start_time", 1916 &start_time), 1917 GNUNET_JSON_spec_timestamp ("end_time", 1918 &end_time), 1919 GNUNET_JSON_spec_fixed_auto ("master_sig", 1920 &master_sig), 1921 GNUNET_JSON_spec_end () 1922 }; 1923 1924 if (GNUNET_OK != 1925 GNUNET_JSON_parse (value, 1926 spec, 1927 &err_name, 1928 &err_line)) 1929 { 1930 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1931 "Invalid input to set wire fee: %s#%u at %u (skipping)\n", 1932 err_name, 1933 err_line, 1934 (unsigned int) idx); 1935 json_dumpf (value, 1936 stderr, 1937 JSON_INDENT (2)); 1938 global_ret = EXIT_FAILURE; 1939 GNUNET_SCHEDULER_shutdown (); 1940 return; 1941 } 1942 gfr = GNUNET_new (struct GlobalFeeRequest); 1943 gfr->idx = idx; 1944 gfr->h = 1945 TALER_EXCHANGE_management_set_global_fees (ctx, 1946 exchange_url, 1947 start_time, 1948 end_time, 1949 &fees, 1950 purse_timeout, 1951 history_expiration, 1952 purse_account_limit, 1953 &master_sig, 1954 &global_fee_cb, 1955 gfr); 1956 GNUNET_CONTAINER_DLL_insert (gfr_head, 1957 gfr_tail, 1958 gfr); 1959 } 1960 1961 1962 /** 1963 * Function called with information about the drain profits operation. 1964 * 1965 * @param cls closure with a `struct DrainProfitsRequest` 1966 * @param mdr response data 1967 */ 1968 static void 1969 drain_profits_cb ( 1970 void *cls, 1971 const struct TALER_EXCHANGE_ManagementDrainResponse *mdr) 1972 { 1973 struct DrainProfitsRequest *dpr = cls; 1974 const struct TALER_EXCHANGE_HttpResponse *hr = &mdr->hr; 1975 1976 if (MHD_HTTP_NO_CONTENT != hr->http_status) 1977 { 1978 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1979 "Upload failed for command %u with status %u: %s (%s)\n", 1980 (unsigned int) dpr->idx, 1981 hr->http_status, 1982 TALER_ErrorCode_get_hint (hr->ec), 1983 hr->hint); 1984 global_ret = EXIT_FAILURE; 1985 } 1986 GNUNET_CONTAINER_DLL_remove (dpr_head, 1987 dpr_tail, 1988 dpr); 1989 GNUNET_free (dpr); 1990 test_shutdown (); 1991 } 1992 1993 1994 /** 1995 * Upload drain profit action. 1996 * 1997 * @param exchange_url base URL of the exchange 1998 * @param idx index of the operation we are performing (for logging) 1999 * @param value arguments for drain profits 2000 */ 2001 static void 2002 upload_drain (const char *exchange_url, 2003 size_t idx, 2004 const json_t *value) 2005 { 2006 struct TALER_WireTransferIdentifierRawP wtid; 2007 struct TALER_MasterSignatureP master_sig; 2008 const char *err_name; 2009 unsigned int err_line; 2010 struct TALER_Amount amount; 2011 struct GNUNET_TIME_Timestamp date; 2012 struct TALER_FullPayto payto_uri; 2013 const char *account_section; 2014 struct DrainProfitsRequest *dpr; 2015 struct GNUNET_JSON_Specification spec[] = { 2016 GNUNET_JSON_spec_fixed_auto ("wtid", 2017 &wtid), 2018 TALER_JSON_spec_amount ("amount", 2019 currency, 2020 &amount), 2021 GNUNET_JSON_spec_timestamp ("date", 2022 &date), 2023 GNUNET_JSON_spec_string ("account_section", 2024 &account_section), 2025 TALER_JSON_spec_full_payto_uri ("payto_uri", 2026 &payto_uri), 2027 GNUNET_JSON_spec_fixed_auto ("master_sig", 2028 &master_sig), 2029 GNUNET_JSON_spec_end () 2030 }; 2031 2032 if (GNUNET_OK != 2033 GNUNET_JSON_parse (value, 2034 spec, 2035 &err_name, 2036 &err_line)) 2037 { 2038 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2039 "Invalid input to drain profits: %s#%u at %u (skipping)\n", 2040 err_name, 2041 err_line, 2042 (unsigned int) idx); 2043 json_dumpf (value, 2044 stderr, 2045 JSON_INDENT (2)); 2046 global_ret = EXIT_FAILURE; 2047 GNUNET_SCHEDULER_shutdown (); 2048 return; 2049 } 2050 dpr = GNUNET_new (struct DrainProfitsRequest); 2051 dpr->idx = idx; 2052 dpr->h = 2053 TALER_EXCHANGE_management_drain_profits (ctx, 2054 exchange_url, 2055 &wtid, 2056 &amount, 2057 date, 2058 account_section, 2059 payto_uri, 2060 &master_sig, 2061 &drain_profits_cb, 2062 dpr); 2063 GNUNET_CONTAINER_DLL_insert (dpr_head, 2064 dpr_tail, 2065 dpr); 2066 } 2067 2068 2069 /** 2070 * Function called with information about the post upload keys operation result. 2071 * 2072 * @param cls closure with a `struct UploadKeysRequest` 2073 * @param mr response data 2074 */ 2075 static void 2076 keys_cb ( 2077 void *cls, 2078 const struct TALER_EXCHANGE_ManagementPostKeysResponse *mr) 2079 { 2080 struct UploadKeysRequest *ukr = cls; 2081 const struct TALER_EXCHANGE_HttpResponse *hr = &mr->hr; 2082 2083 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2084 { 2085 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2086 "Upload failed for command %u with status %u: %s (%s)\n", 2087 (unsigned int) ukr->idx, 2088 hr->http_status, 2089 TALER_ErrorCode_get_hint (hr->ec), 2090 hr->hint); 2091 global_ret = EXIT_FAILURE; 2092 } 2093 GNUNET_CONTAINER_DLL_remove (ukr_head, 2094 ukr_tail, 2095 ukr); 2096 GNUNET_free (ukr); 2097 test_shutdown (); 2098 } 2099 2100 2101 /** 2102 * Upload (denomination and signing) key master signatures. 2103 * 2104 * @param exchange_url base URL of the exchange 2105 * @param idx index of the operation we are performing (for logging) 2106 * @param value arguments for POSTing keys 2107 */ 2108 static void 2109 upload_keys (const char *exchange_url, 2110 size_t idx, 2111 const json_t *value) 2112 { 2113 struct TALER_EXCHANGE_ManagementPostKeysData pkd; 2114 struct UploadKeysRequest *ukr; 2115 const char *err_name; 2116 unsigned int err_line; 2117 const json_t *denom_sigs; 2118 const json_t *signkey_sigs; 2119 struct GNUNET_JSON_Specification spec[] = { 2120 GNUNET_JSON_spec_array_const ("denom_sigs", 2121 &denom_sigs), 2122 GNUNET_JSON_spec_array_const ("signkey_sigs", 2123 &signkey_sigs), 2124 GNUNET_JSON_spec_end () 2125 }; 2126 bool ok = true; 2127 2128 if (GNUNET_OK != 2129 GNUNET_JSON_parse (value, 2130 spec, 2131 &err_name, 2132 &err_line)) 2133 { 2134 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2135 "Invalid input to 'upload': %s#%u (skipping)\n", 2136 err_name, 2137 err_line); 2138 json_dumpf (value, 2139 stderr, 2140 JSON_INDENT (2)); 2141 global_ret = EXIT_FAILURE; 2142 GNUNET_SCHEDULER_shutdown (); 2143 return; 2144 } 2145 pkd.num_sign_sigs = json_array_size (signkey_sigs); 2146 pkd.num_denom_sigs = json_array_size (denom_sigs); 2147 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2148 "Uploading %u denomination and %u signing key signatures\n", 2149 pkd.num_denom_sigs, 2150 pkd.num_sign_sigs); 2151 pkd.sign_sigs = GNUNET_new_array ( 2152 pkd.num_sign_sigs, 2153 struct TALER_EXCHANGE_SigningKeySignature); 2154 pkd.denom_sigs = GNUNET_new_array ( 2155 pkd.num_denom_sigs, 2156 struct TALER_EXCHANGE_DenominationKeySignature); 2157 for (unsigned int i = 0; i<pkd.num_sign_sigs; i++) 2158 { 2159 struct TALER_EXCHANGE_SigningKeySignature *ss = &pkd.sign_sigs[i]; 2160 json_t *val = json_array_get (signkey_sigs, 2161 i); 2162 struct GNUNET_JSON_Specification ispec[] = { 2163 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 2164 &ss->exchange_pub), 2165 GNUNET_JSON_spec_fixed_auto ("master_sig", 2166 &ss->master_sig), 2167 GNUNET_JSON_spec_end () 2168 }; 2169 2170 if (GNUNET_OK != 2171 GNUNET_JSON_parse (val, 2172 ispec, 2173 &err_name, 2174 &err_line)) 2175 { 2176 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2177 "Invalid input for signkey validity: %s#%u at %u (aborting)\n", 2178 err_name, 2179 err_line, 2180 i); 2181 json_dumpf (val, 2182 stderr, 2183 JSON_INDENT (2)); 2184 ok = false; 2185 } 2186 } 2187 for (unsigned int i = 0; i<pkd.num_denom_sigs; i++) 2188 { 2189 struct TALER_EXCHANGE_DenominationKeySignature *ds = &pkd.denom_sigs[i]; 2190 json_t *val = json_array_get (denom_sigs, 2191 i); 2192 struct GNUNET_JSON_Specification ispec[] = { 2193 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 2194 &ds->h_denom_pub), 2195 GNUNET_JSON_spec_fixed_auto ("master_sig", 2196 &ds->master_sig), 2197 GNUNET_JSON_spec_end () 2198 }; 2199 2200 if (GNUNET_OK != 2201 GNUNET_JSON_parse (val, 2202 ispec, 2203 &err_name, 2204 &err_line)) 2205 { 2206 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2207 "Invalid input for denomination validity: %s#%u at %u (aborting)\n", 2208 err_name, 2209 err_line, 2210 i); 2211 json_dumpf (val, 2212 stderr, 2213 JSON_INDENT (2)); 2214 ok = false; 2215 } 2216 } 2217 2218 if (ok) 2219 { 2220 ukr = GNUNET_new (struct UploadKeysRequest); 2221 ukr->idx = idx; 2222 ukr->h = 2223 TALER_EXCHANGE_post_management_keys (ctx, 2224 exchange_url, 2225 &pkd, 2226 &keys_cb, 2227 ukr); 2228 GNUNET_CONTAINER_DLL_insert (ukr_head, 2229 ukr_tail, 2230 ukr); 2231 } 2232 else 2233 { 2234 global_ret = EXIT_FAILURE; 2235 GNUNET_SCHEDULER_shutdown (); 2236 } 2237 GNUNET_free (pkd.sign_sigs); 2238 GNUNET_free (pkd.denom_sigs); 2239 } 2240 2241 2242 /** 2243 * Function called with information about the post upload extensions operation result. 2244 * 2245 * @param cls closure with a `struct UploadExtensionsRequest` 2246 * @param er response data 2247 */ 2248 static void 2249 extensions_cb ( 2250 void *cls, 2251 const struct TALER_EXCHANGE_ManagementPostExtensionsResponse *er) 2252 { 2253 struct UploadExtensionsRequest *uer = cls; 2254 const struct TALER_EXCHANGE_HttpResponse *hr = &er->hr; 2255 2256 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2257 { 2258 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2259 "Upload failed for command %u with status %u: %s (%s)\n", 2260 (unsigned int) uer->idx, 2261 hr->http_status, 2262 TALER_ErrorCode_get_hint (hr->ec), 2263 hr->hint); 2264 global_ret = EXIT_FAILURE; 2265 } 2266 GNUNET_CONTAINER_DLL_remove (uer_head, 2267 uer_tail, 2268 uer); 2269 GNUNET_free (uer); 2270 test_shutdown (); 2271 } 2272 2273 2274 /** 2275 * Upload extension configuration 2276 * 2277 * @param exchange_url base URL of the exchange 2278 * @param idx index of the operation we are performing (for logging) 2279 * @param value arguments for POSTing configurations of extensions 2280 */ 2281 static void 2282 upload_extensions (const char *exchange_url, 2283 size_t idx, 2284 const json_t *value) 2285 { 2286 const json_t *extensions; 2287 struct TALER_MasterSignatureP sig; 2288 const char *err_name; 2289 unsigned int err_line; 2290 struct GNUNET_JSON_Specification spec[] = { 2291 GNUNET_JSON_spec_object_const ("extensions", 2292 &extensions), 2293 GNUNET_JSON_spec_fixed_auto ("extensions_sig", 2294 &sig), 2295 GNUNET_JSON_spec_end () 2296 }; 2297 2298 /* 1. Parse the signed extensions */ 2299 if (GNUNET_OK != 2300 GNUNET_JSON_parse (value, 2301 spec, 2302 &err_name, 2303 &err_line)) 2304 { 2305 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2306 "Invalid input to set extensions: %s#%u at %u (skipping)\n", 2307 err_name, 2308 err_line, 2309 (unsigned int) idx); 2310 json_dumpf (value, 2311 stderr, 2312 JSON_INDENT (2)); 2313 global_ret = EXIT_FAILURE; 2314 GNUNET_SCHEDULER_shutdown (); 2315 return; 2316 } 2317 2318 /* 2. Verify the signature */ 2319 { 2320 struct TALER_ExtensionManifestsHashP h_manifests; 2321 2322 if (GNUNET_OK != 2323 TALER_JSON_extensions_manifests_hash (extensions, 2324 &h_manifests)) 2325 { 2326 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2327 "couldn't hash extensions' manifests\n"); 2328 global_ret = EXIT_FAILURE; 2329 GNUNET_SCHEDULER_shutdown (); 2330 return; 2331 } 2332 2333 if (GNUNET_OK != 2334 load_offline_key (GNUNET_NO)) 2335 return; 2336 2337 if (GNUNET_OK != TALER_exchange_offline_extension_manifests_hash_verify ( 2338 &h_manifests, 2339 &master_pub, 2340 &sig)) 2341 { 2342 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2343 "invalid signature for extensions\n"); 2344 global_ret = EXIT_FAILURE; 2345 GNUNET_SCHEDULER_shutdown (); 2346 return; 2347 } 2348 } 2349 2350 /* 3. Upload the extensions */ 2351 { 2352 struct TALER_EXCHANGE_ManagementPostExtensionsData ped = { 2353 .extensions = extensions, 2354 .extensions_sig = sig, 2355 }; 2356 struct UploadExtensionsRequest *uer 2357 = GNUNET_new (struct UploadExtensionsRequest); 2358 2359 uer->idx = idx; 2360 uer->h = TALER_EXCHANGE_management_post_extensions ( 2361 ctx, 2362 exchange_url, 2363 &ped, 2364 &extensions_cb, 2365 uer); 2366 GNUNET_CONTAINER_DLL_insert (uer_head, 2367 uer_tail, 2368 uer); 2369 } 2370 } 2371 2372 2373 /** 2374 * Function called with information about the add partner operation. 2375 * 2376 * @param cls closure with a `struct PartnerAddRequest` 2377 * @param apr response data 2378 */ 2379 static void 2380 add_partner_cb ( 2381 void *cls, 2382 const struct TALER_EXCHANGE_ManagementAddPartnerResponse *apr) 2383 { 2384 struct PartnerAddRequest *par = cls; 2385 const struct TALER_EXCHANGE_HttpResponse *hr = &apr->hr; 2386 2387 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2388 { 2389 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2390 "Upload failed for command %u with status %u: %s (%s)\n", 2391 (unsigned int) par->idx, 2392 hr->http_status, 2393 TALER_ErrorCode_get_hint (hr->ec), 2394 hr->hint); 2395 global_ret = EXIT_FAILURE; 2396 } 2397 GNUNET_CONTAINER_DLL_remove (par_head, 2398 par_tail, 2399 par); 2400 GNUNET_free (par); 2401 test_shutdown (); 2402 } 2403 2404 2405 /** 2406 * Add partner action. 2407 * 2408 * @param exchange_url base URL of the exchange 2409 * @param idx index of the operation we are performing (for logging) 2410 * @param value arguments for add partner 2411 */ 2412 static void 2413 add_partner (const char *exchange_url, 2414 size_t idx, 2415 const json_t *value) 2416 { 2417 struct TALER_MasterPublicKeyP partner_pub; 2418 struct GNUNET_TIME_Timestamp start_date; 2419 struct GNUNET_TIME_Timestamp end_date; 2420 struct GNUNET_TIME_Relative wad_frequency; 2421 struct TALER_Amount wad_fee; 2422 const char *partner_base_url; 2423 struct TALER_MasterSignatureP master_sig; 2424 struct PartnerAddRequest *par; 2425 struct GNUNET_JSON_Specification spec[] = { 2426 GNUNET_JSON_spec_fixed_auto ("partner_pub", 2427 &partner_pub), 2428 TALER_JSON_spec_amount ("wad_fee", 2429 currency, 2430 &wad_fee), 2431 GNUNET_JSON_spec_relative_time ("wad_frequency", 2432 &wad_frequency), 2433 GNUNET_JSON_spec_timestamp ("start_date", 2434 &start_date), 2435 GNUNET_JSON_spec_timestamp ("end_date", 2436 &end_date), 2437 TALER_JSON_spec_web_url ("partner_base_url", 2438 &partner_base_url), 2439 GNUNET_JSON_spec_fixed_auto ("master_sig", 2440 &master_sig), 2441 GNUNET_JSON_spec_end () 2442 }; 2443 const char *err_name; 2444 unsigned int err_line; 2445 2446 if (GNUNET_OK != 2447 GNUNET_JSON_parse (value, 2448 spec, 2449 &err_name, 2450 &err_line)) 2451 { 2452 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2453 "Invalid input to add partner: %s#%u at %u (skipping)\n", 2454 err_name, 2455 err_line, 2456 (unsigned int) idx); 2457 json_dumpf (value, 2458 stderr, 2459 JSON_INDENT (2)); 2460 global_ret = EXIT_FAILURE; 2461 GNUNET_SCHEDULER_shutdown (); 2462 return; 2463 } 2464 par = GNUNET_new (struct PartnerAddRequest); 2465 par->idx = idx; 2466 par->h = 2467 TALER_EXCHANGE_management_add_partner (ctx, 2468 exchange_url, 2469 &partner_pub, 2470 start_date, 2471 end_date, 2472 wad_frequency, 2473 &wad_fee, 2474 partner_base_url, 2475 &master_sig, 2476 &add_partner_cb, 2477 par); 2478 GNUNET_CONTAINER_DLL_insert (par_head, 2479 par_tail, 2480 par); 2481 } 2482 2483 2484 /** 2485 * Function called with information about the AML officer update operation. 2486 * 2487 * @param cls closure with a `struct AmlStaffRequest` 2488 * @param ar response data 2489 */ 2490 static void 2491 update_aml_officer_cb ( 2492 void *cls, 2493 const struct TALER_EXCHANGE_ManagementUpdateAmlOfficerResponse *ar) 2494 { 2495 struct AmlStaffRequest *asr = cls; 2496 const struct TALER_EXCHANGE_HttpResponse *hr = &ar->hr; 2497 2498 if (MHD_HTTP_NO_CONTENT != hr->http_status) 2499 { 2500 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2501 "Upload failed for command %u with status %u: %s (%s)\n", 2502 (unsigned int) asr->idx, 2503 hr->http_status, 2504 TALER_ErrorCode_get_hint (hr->ec), 2505 hr->hint); 2506 global_ret = EXIT_FAILURE; 2507 } 2508 GNUNET_CONTAINER_DLL_remove (asr_head, 2509 asr_tail, 2510 asr); 2511 GNUNET_free (asr); 2512 test_shutdown (); 2513 } 2514 2515 2516 /** 2517 * Upload AML staff action. 2518 * 2519 * @param exchange_url base URL of the exchange 2520 * @param idx index of the operation we are performing (for logging) 2521 * @param value arguments for AML staff change 2522 */ 2523 static void 2524 update_aml_staff (const char *exchange_url, 2525 size_t idx, 2526 const json_t *value) 2527 { 2528 struct TALER_AmlOfficerPublicKeyP officer_pub; 2529 const char *officer_name; 2530 struct GNUNET_TIME_Timestamp change_date; 2531 bool is_active; 2532 bool read_only; 2533 struct TALER_MasterSignatureP master_sig; 2534 struct AmlStaffRequest *asr; 2535 struct GNUNET_JSON_Specification spec[] = { 2536 GNUNET_JSON_spec_fixed_auto ("officer_pub", 2537 &officer_pub), 2538 GNUNET_JSON_spec_timestamp ("change_date", 2539 &change_date), 2540 GNUNET_JSON_spec_bool ("is_active", 2541 &is_active), 2542 GNUNET_JSON_spec_bool ("read_only", 2543 &read_only), 2544 GNUNET_JSON_spec_string ("officer_name", 2545 &officer_name), 2546 GNUNET_JSON_spec_fixed_auto ("master_sig", 2547 &master_sig), 2548 GNUNET_JSON_spec_end () 2549 }; 2550 const char *err_name; 2551 unsigned int err_line; 2552 2553 if (GNUNET_OK != 2554 GNUNET_JSON_parse (value, 2555 spec, 2556 &err_name, 2557 &err_line)) 2558 { 2559 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2560 "Invalid input to AML staff update: %s#%u at %u (skipping)\n", 2561 err_name, 2562 err_line, 2563 (unsigned int) idx); 2564 json_dumpf (value, 2565 stderr, 2566 JSON_INDENT (2)); 2567 global_ret = EXIT_FAILURE; 2568 GNUNET_SCHEDULER_shutdown (); 2569 return; 2570 } 2571 asr = GNUNET_new (struct AmlStaffRequest); 2572 asr->idx = idx; 2573 asr->h = 2574 TALER_EXCHANGE_management_update_aml_officer (ctx, 2575 exchange_url, 2576 &officer_pub, 2577 officer_name, 2578 change_date, 2579 is_active, 2580 read_only, 2581 &master_sig, 2582 &update_aml_officer_cb, 2583 asr); 2584 GNUNET_CONTAINER_DLL_insert (asr_head, 2585 asr_tail, 2586 asr); 2587 } 2588 2589 2590 /** 2591 * Perform uploads based on the JSON in #out. 2592 * 2593 * @param exchange_url base URL of the exchange to use 2594 */ 2595 static void 2596 trigger_upload (const char *exchange_url) 2597 { 2598 struct UploadHandler uhs[] = { 2599 { 2600 .key = OP_REVOKE_DENOMINATION, 2601 .cb = &upload_denom_revocation 2602 }, 2603 { 2604 .key = OP_REVOKE_SIGNKEY, 2605 .cb = &upload_signkey_revocation 2606 }, 2607 { 2608 .key = OP_ENABLE_AUDITOR, 2609 .cb = &upload_auditor_add 2610 }, 2611 { 2612 .key = OP_DISABLE_AUDITOR, 2613 .cb = &upload_auditor_del 2614 }, 2615 { 2616 .key = OP_ENABLE_WIRE, 2617 .cb = &upload_wire_add 2618 }, 2619 { 2620 .key = OP_DISABLE_WIRE, 2621 .cb = &upload_wire_del 2622 }, 2623 { 2624 .key = OP_SET_WIRE_FEE, 2625 .cb = &upload_wire_fee 2626 }, 2627 { 2628 .key = OP_SET_GLOBAL_FEE, 2629 .cb = &upload_global_fee 2630 }, 2631 { 2632 .key = OP_UPLOAD_SIGS, 2633 .cb = &upload_keys 2634 }, 2635 { 2636 .key = OP_DRAIN_PROFITS, 2637 .cb = &upload_drain 2638 }, 2639 { 2640 .key = OP_EXTENSIONS, 2641 .cb = &upload_extensions 2642 }, 2643 { 2644 .key = OP_UPDATE_AML_STAFF, 2645 .cb = &update_aml_staff 2646 }, 2647 { 2648 .key = OP_ADD_PARTNER, 2649 .cb = &add_partner 2650 }, 2651 /* array termination */ 2652 { 2653 .key = NULL 2654 } 2655 }; 2656 size_t index; 2657 json_t *obj; 2658 2659 json_array_foreach (out, index, obj) { 2660 bool found = false; 2661 const char *key; 2662 const json_t *value; 2663 2664 key = json_string_value (json_object_get (obj, "operation")); 2665 value = json_object_get (obj, "arguments"); 2666 if (NULL == key) 2667 { 2668 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2669 "Malformed JSON input\n"); 2670 global_ret = EXIT_FAILURE; 2671 GNUNET_SCHEDULER_shutdown (); 2672 return; 2673 } 2674 /* block of code that uses key and value */ 2675 for (unsigned int i = 0; NULL != uhs[i].key; i++) 2676 { 2677 if (0 == strcasecmp (key, 2678 uhs[i].key)) 2679 { 2680 2681 found = true; 2682 uhs[i].cb (exchange_url, 2683 index, 2684 value); 2685 break; 2686 } 2687 } 2688 if (! found) 2689 { 2690 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2691 "Upload does not know how to handle `%s'\n", 2692 key); 2693 global_ret = EXIT_FAILURE; 2694 GNUNET_SCHEDULER_shutdown (); 2695 return; 2696 } 2697 } 2698 } 2699 2700 2701 /** 2702 * Upload operation result (signatures) to exchange. 2703 * 2704 * @param args the array of command-line arguments to process next 2705 */ 2706 static void 2707 do_upload (char *const *args) 2708 { 2709 (void) args; 2710 if (NULL != in) 2711 { 2712 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2713 "Downloaded data was not consumed, refusing upload\n"); 2714 GNUNET_SCHEDULER_shutdown (); 2715 global_ret = EXIT_FAILURE; 2716 return; 2717 } 2718 if (NULL == out) 2719 { 2720 json_error_t err; 2721 2722 out = json_loadf (stdin, 2723 JSON_REJECT_DUPLICATES, 2724 &err); 2725 if (NULL == out) 2726 { 2727 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2728 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 2729 err.text, 2730 err.line, 2731 err.source, 2732 err.position); 2733 GNUNET_SCHEDULER_shutdown (); 2734 global_ret = EXIT_FAILURE; 2735 return; 2736 } 2737 } 2738 if (! json_is_array (out)) 2739 { 2740 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2741 "Error: expected JSON array for `upload` command\n"); 2742 GNUNET_SCHEDULER_shutdown (); 2743 global_ret = EXIT_FAILURE; 2744 return; 2745 } 2746 if ( (NULL == CFG_exchange_url) && 2747 (GNUNET_OK != 2748 GNUNET_CONFIGURATION_get_value_string (kcfg, 2749 "exchange", 2750 "BASE_URL", 2751 &CFG_exchange_url)) ) 2752 { 2753 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 2754 "exchange", 2755 "BASE_URL"); 2756 global_ret = EXIT_NOTCONFIGURED; 2757 GNUNET_SCHEDULER_shutdown (); 2758 return; 2759 } 2760 trigger_upload (CFG_exchange_url); 2761 json_decref (out); 2762 out = NULL; 2763 } 2764 2765 2766 /** 2767 * Revoke denomination key. 2768 * 2769 * @param args the array of command-line arguments to process next; 2770 * args[0] must be the hash of the denomination key to revoke 2771 */ 2772 static void 2773 do_revoke_denomination_key (char *const *args) 2774 { 2775 struct TALER_DenominationHashP h_denom_pub; 2776 struct TALER_MasterSignatureP master_sig; 2777 2778 if (NULL != in) 2779 { 2780 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2781 "Downloaded data was not consumed, refusing revocation\n"); 2782 GNUNET_SCHEDULER_shutdown (); 2783 global_ret = EXIT_FAILURE; 2784 return; 2785 } 2786 if ( (NULL == args[0]) || 2787 (GNUNET_OK != 2788 GNUNET_STRINGS_string_to_data (args[0], 2789 strlen (args[0]), 2790 &h_denom_pub, 2791 sizeof (h_denom_pub))) ) 2792 { 2793 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2794 "You must specify a denomination key with this subcommand\n"); 2795 GNUNET_SCHEDULER_shutdown (); 2796 global_ret = EXIT_INVALIDARGUMENT; 2797 return; 2798 } 2799 if (GNUNET_OK != 2800 load_offline_key (GNUNET_NO)) 2801 return; 2802 TALER_exchange_offline_denomination_revoke_sign (&h_denom_pub, 2803 &master_priv, 2804 &master_sig); 2805 output_operation (OP_REVOKE_DENOMINATION, 2806 GNUNET_JSON_PACK ( 2807 GNUNET_JSON_pack_data_auto ("h_denom_pub", 2808 &h_denom_pub), 2809 GNUNET_JSON_pack_data_auto ("master_sig", 2810 &master_sig))); 2811 next (args + 1); 2812 } 2813 2814 2815 /** 2816 * Revoke signkey. 2817 * 2818 * @param args the array of command-line arguments to process next; 2819 * args[0] must be the hash of the denomination key to revoke 2820 */ 2821 static void 2822 do_revoke_signkey (char *const *args) 2823 { 2824 struct TALER_ExchangePublicKeyP exchange_pub; 2825 struct TALER_MasterSignatureP master_sig; 2826 2827 if (NULL != in) 2828 { 2829 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2830 "Downloaded data was not consumed, refusing revocation\n"); 2831 GNUNET_SCHEDULER_shutdown (); 2832 global_ret = EXIT_FAILURE; 2833 return; 2834 } 2835 if ( (NULL == args[0]) || 2836 (GNUNET_OK != 2837 GNUNET_STRINGS_string_to_data (args[0], 2838 strlen (args[0]), 2839 &exchange_pub, 2840 sizeof (exchange_pub))) ) 2841 { 2842 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2843 "You must specify an exchange signing key with this subcommand\n"); 2844 GNUNET_SCHEDULER_shutdown (); 2845 global_ret = EXIT_INVALIDARGUMENT; 2846 return; 2847 } 2848 if (GNUNET_OK != 2849 load_offline_key (GNUNET_NO)) 2850 return; 2851 TALER_exchange_offline_signkey_revoke_sign (&exchange_pub, 2852 &master_priv, 2853 &master_sig); 2854 output_operation (OP_REVOKE_SIGNKEY, 2855 GNUNET_JSON_PACK ( 2856 GNUNET_JSON_pack_data_auto ("exchange_pub", 2857 &exchange_pub), 2858 GNUNET_JSON_pack_data_auto ("master_sig", 2859 &master_sig))); 2860 next (args + 1); 2861 } 2862 2863 2864 /** 2865 * Add auditor. 2866 * 2867 * @param args the array of command-line arguments to process next; 2868 * args[0] must be the auditor's public key, args[1] the auditor's 2869 * API base URL, and args[2] the auditor's name. 2870 */ 2871 static void 2872 do_add_auditor (char *const *args) 2873 { 2874 struct TALER_MasterSignatureP master_sig; 2875 struct TALER_AuditorPublicKeyP auditor_pub; 2876 struct GNUNET_TIME_Timestamp now; 2877 2878 if (NULL != in) 2879 { 2880 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2881 "Downloaded data was not consumed, not adding auditor\n"); 2882 GNUNET_SCHEDULER_shutdown (); 2883 global_ret = EXIT_FAILURE; 2884 return; 2885 } 2886 if ( (NULL == args[0]) || 2887 (GNUNET_OK != 2888 GNUNET_STRINGS_string_to_data (args[0], 2889 strlen (args[0]), 2890 &auditor_pub, 2891 sizeof (auditor_pub))) ) 2892 { 2893 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2894 "You must specify an auditor public key as first argument for this subcommand\n"); 2895 GNUNET_SCHEDULER_shutdown (); 2896 global_ret = EXIT_INVALIDARGUMENT; 2897 return; 2898 } 2899 2900 if ( (NULL == args[1]) || 2901 (0 != strncmp ("http", 2902 args[1], 2903 strlen ("http"))) || 2904 (NULL == args[2]) ) 2905 { 2906 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2907 "You must specify an auditor URI and auditor name as 2nd and 3rd arguments to this subcommand\n"); 2908 GNUNET_SCHEDULER_shutdown (); 2909 global_ret = EXIT_INVALIDARGUMENT; 2910 return; 2911 } 2912 if (GNUNET_OK != 2913 load_offline_key (GNUNET_NO)) 2914 return; 2915 now = GNUNET_TIME_timestamp_get (); 2916 TALER_exchange_offline_auditor_add_sign (&auditor_pub, 2917 args[1], 2918 now, 2919 &master_priv, 2920 &master_sig); 2921 output_operation (OP_ENABLE_AUDITOR, 2922 GNUNET_JSON_PACK ( 2923 GNUNET_JSON_pack_string ("auditor_url", 2924 args[1]), 2925 GNUNET_JSON_pack_string ("auditor_name", 2926 args[2]), 2927 GNUNET_JSON_pack_timestamp ("validity_start", 2928 now), 2929 GNUNET_JSON_pack_data_auto ("auditor_pub", 2930 &auditor_pub), 2931 GNUNET_JSON_pack_data_auto ("master_sig", 2932 &master_sig))); 2933 next (args + 3); 2934 } 2935 2936 2937 /** 2938 * Disable auditor account. 2939 * 2940 * @param args the array of command-line arguments to process next; 2941 * args[0] must be the hash of the denomination key to revoke 2942 */ 2943 static void 2944 do_del_auditor (char *const *args) 2945 { 2946 struct TALER_MasterSignatureP master_sig; 2947 struct TALER_AuditorPublicKeyP auditor_pub; 2948 struct GNUNET_TIME_Timestamp now; 2949 2950 if (NULL != in) 2951 { 2952 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2953 "Downloaded data was not consumed, not deleting auditor account\n"); 2954 GNUNET_SCHEDULER_shutdown (); 2955 global_ret = EXIT_FAILURE; 2956 return; 2957 } 2958 if ( (NULL == args[0]) || 2959 (GNUNET_OK != 2960 GNUNET_STRINGS_string_to_data (args[0], 2961 strlen (args[0]), 2962 &auditor_pub, 2963 sizeof (auditor_pub))) ) 2964 { 2965 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2966 "You must specify an auditor public key as first argument for this subcommand\n"); 2967 GNUNET_SCHEDULER_shutdown (); 2968 global_ret = EXIT_INVALIDARGUMENT; 2969 return; 2970 } 2971 if (GNUNET_OK != 2972 load_offline_key (GNUNET_NO)) 2973 return; 2974 now = GNUNET_TIME_timestamp_get (); 2975 TALER_exchange_offline_auditor_del_sign (&auditor_pub, 2976 now, 2977 &master_priv, 2978 &master_sig); 2979 output_operation (OP_DISABLE_AUDITOR, 2980 GNUNET_JSON_PACK ( 2981 GNUNET_JSON_pack_data_auto ("auditor_pub", 2982 &auditor_pub), 2983 GNUNET_JSON_pack_timestamp ("validity_end", 2984 now), 2985 GNUNET_JSON_pack_data_auto ("master_sig", 2986 &master_sig))); 2987 next (args + 1); 2988 } 2989 2990 2991 /** 2992 * Parse account restriction. 2993 * 2994 * @param args the array of command-line arguments to process next 2995 * @param[in,out] restrictions JSON array to update 2996 * @return -1 on error, otherwise number of arguments from @a args that were used 2997 */ 2998 static int 2999 parse_restriction (char *const *args, 3000 json_t *restrictions) 3001 { 3002 if (NULL == args[0]) 3003 { 3004 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3005 "Restriction TYPE argument missing\n"); 3006 return -1; 3007 } 3008 if (0 == strcmp (args[0], 3009 "deny")) 3010 { 3011 GNUNET_assert (0 == 3012 json_array_append_new ( 3013 restrictions, 3014 GNUNET_JSON_PACK ( 3015 GNUNET_JSON_pack_string ("type", 3016 "deny")))); 3017 return 1; 3018 } 3019 if (0 == strcmp (args[0], 3020 "regex")) 3021 { 3022 json_t *i18n; 3023 json_error_t err; 3024 3025 if ( (NULL == args[1]) || 3026 (NULL == args[2]) || 3027 (NULL == args[3]) ) 3028 { 3029 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3030 "Mandatory arguments for restriction of type `regex' missing (REGEX, HINT, HINT-I18 required)\n"); 3031 return -1; 3032 } 3033 { 3034 regex_t ex; 3035 3036 if (0 != regcomp (&ex, 3037 args[1], 3038 REG_NOSUB | REG_EXTENDED)) 3039 { 3040 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3041 "Invalid regular expression `%s'\n", 3042 args[1]); 3043 return -1; 3044 } 3045 regfree (&ex); 3046 } 3047 3048 i18n = json_loads (args[3], 3049 JSON_REJECT_DUPLICATES, 3050 &err); 3051 if (NULL == i18n) 3052 { 3053 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3054 "Invalid JSON for restriction of type `regex': `%s` at %d\n", 3055 args[3], 3056 err.position); 3057 return -1; 3058 } 3059 GNUNET_assert (0 == 3060 json_array_append_new ( 3061 restrictions, 3062 GNUNET_JSON_PACK ( 3063 GNUNET_JSON_pack_string ("type", 3064 "regex"), 3065 GNUNET_JSON_pack_string ("payto_regex", 3066 args[1]), 3067 GNUNET_JSON_pack_string ("human_hint", 3068 args[2]), 3069 GNUNET_JSON_pack_object_steal ("human_hint_i18n", 3070 i18n) 3071 ))); 3072 return 4; 3073 } 3074 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3075 "Restriction TYPE `%s' unsupported\n", 3076 args[0]); 3077 return -1; 3078 } 3079 3080 3081 /** 3082 * Add wire account. 3083 * 3084 * @param args the array of command-line arguments to process next; 3085 * args[0] must be the hash of the denomination key to revoke 3086 */ 3087 static void 3088 do_add_wire (char *const *args) 3089 { 3090 struct TALER_MasterSignatureP master_sig_add; 3091 struct TALER_MasterSignatureP master_sig_wire; 3092 struct GNUNET_TIME_Timestamp now; 3093 const char *conversion_url = NULL; 3094 const char *bank_label = NULL; 3095 int64_t priority = 0; 3096 json_t *debit_restrictions; 3097 json_t *credit_restrictions; 3098 unsigned int num_args = 1; 3099 struct TALER_FullPayto payto_uri = { 3100 .full_payto = args[0] 3101 }; 3102 3103 if (NULL != in) 3104 { 3105 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3106 "Downloaded data was not consumed, not adding wire account\n"); 3107 GNUNET_SCHEDULER_shutdown (); 3108 global_ret = EXIT_FAILURE; 3109 return; 3110 } 3111 if (NULL == args[0]) 3112 { 3113 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3114 "You must specify a payto://-URI with this subcommand\n"); 3115 GNUNET_SCHEDULER_shutdown (); 3116 global_ret = EXIT_INVALIDARGUMENT; 3117 return; 3118 } 3119 if (GNUNET_OK != 3120 load_offline_key (GNUNET_NO)) 3121 return; 3122 { 3123 char *msg = TALER_payto_validate (payto_uri); 3124 3125 if (NULL != msg) 3126 { 3127 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3128 "payto URI `%s' is malformed: %s\n", 3129 payto_uri.full_payto, 3130 msg); 3131 GNUNET_free (msg); 3132 GNUNET_SCHEDULER_shutdown (); 3133 global_ret = EXIT_INVALIDARGUMENT; 3134 return; 3135 } 3136 } 3137 now = GNUNET_TIME_timestamp_get (); 3138 { 3139 char *wire_method; 3140 3141 wire_method = TALER_payto_get_method (payto_uri.full_payto); 3142 if (NULL == wire_method) 3143 { 3144 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3145 "payto:// URI `%s' is malformed\n", 3146 payto_uri.full_payto); 3147 global_ret = EXIT_INVALIDARGUMENT; 3148 GNUNET_SCHEDULER_shutdown (); 3149 return; 3150 } 3151 GNUNET_free (wire_method); 3152 } 3153 debit_restrictions = json_array (); 3154 GNUNET_assert (NULL != debit_restrictions); 3155 credit_restrictions = json_array (); 3156 GNUNET_assert (NULL != credit_restrictions); 3157 while (NULL != args[num_args]) 3158 { 3159 if (0 == strcmp (args[num_args], 3160 "conversion-url")) 3161 { 3162 num_args++; 3163 conversion_url = args[num_args]; 3164 if (NULL == conversion_url) 3165 { 3166 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3167 "'conversion-url' requires an argument\n"); 3168 global_ret = EXIT_INVALIDARGUMENT; 3169 GNUNET_SCHEDULER_shutdown (); 3170 json_decref (debit_restrictions); 3171 json_decref (credit_restrictions); 3172 return; 3173 } 3174 if (! TALER_is_web_url (conversion_url)) 3175 { 3176 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3177 "'conversion-url' must refer to HTTP(S) endpoint, `%s' is invalid\n", 3178 conversion_url); 3179 global_ret = EXIT_INVALIDARGUMENT; 3180 GNUNET_SCHEDULER_shutdown (); 3181 json_decref (debit_restrictions); 3182 json_decref (credit_restrictions); 3183 return; 3184 } 3185 num_args++; 3186 continue; 3187 } 3188 if (0 == strcmp (args[num_args], 3189 "credit-restriction")) 3190 { 3191 int iret; 3192 3193 num_args++; 3194 iret = parse_restriction (&args[num_args], 3195 credit_restrictions); 3196 if (iret <= 0) 3197 { 3198 global_ret = EXIT_INVALIDARGUMENT; 3199 GNUNET_SCHEDULER_shutdown (); 3200 json_decref (debit_restrictions); 3201 json_decref (credit_restrictions); 3202 return; 3203 } 3204 num_args += iret; 3205 continue; 3206 } 3207 if (0 == strcmp (args[num_args], 3208 "debit-restriction")) 3209 { 3210 int iret; 3211 3212 num_args++; 3213 iret = parse_restriction (&args[num_args], 3214 debit_restrictions); 3215 if (iret <= 0) 3216 { 3217 global_ret = EXIT_INVALIDARGUMENT; 3218 GNUNET_SCHEDULER_shutdown (); 3219 json_decref (debit_restrictions); 3220 json_decref (credit_restrictions); 3221 return; 3222 } 3223 num_args += iret; 3224 continue; 3225 } 3226 if (0 == strcmp (args[num_args], 3227 "display-hint")) 3228 { 3229 long long p; 3230 char dummy; 3231 3232 num_args++; 3233 if ( (NULL == args[num_args]) || 3234 (NULL == args[num_args + 1]) ) 3235 { 3236 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3237 "'display-hint' requires at least two arguments (priority and label)\n"); 3238 global_ret = EXIT_INVALIDARGUMENT; 3239 GNUNET_SCHEDULER_shutdown (); 3240 json_decref (debit_restrictions); 3241 json_decref (credit_restrictions); 3242 return; 3243 } 3244 if (1 != sscanf (args[num_args], 3245 "%lld%c", 3246 &p, 3247 &dummy)) 3248 { 3249 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3250 "Priority argument `%s' is not a number\n", 3251 args[num_args]); 3252 global_ret = EXIT_INVALIDARGUMENT; 3253 GNUNET_SCHEDULER_shutdown (); 3254 json_decref (debit_restrictions); 3255 json_decref (credit_restrictions); 3256 return; 3257 } 3258 priority = (int64_t) p; 3259 num_args++; 3260 bank_label = args[num_args]; 3261 num_args++; 3262 continue; 3263 } 3264 break; 3265 } 3266 TALER_exchange_offline_wire_add_sign (payto_uri, 3267 conversion_url, 3268 debit_restrictions, 3269 credit_restrictions, 3270 now, 3271 &master_priv, 3272 &master_sig_add); 3273 TALER_exchange_wire_signature_make (payto_uri, 3274 conversion_url, 3275 debit_restrictions, 3276 credit_restrictions, 3277 &master_priv, 3278 &master_sig_wire); 3279 output_operation (OP_ENABLE_WIRE, 3280 GNUNET_JSON_PACK ( 3281 TALER_JSON_pack_full_payto ("payto_uri", 3282 payto_uri), 3283 GNUNET_JSON_pack_array_steal ("debit_restrictions", 3284 debit_restrictions), 3285 GNUNET_JSON_pack_array_steal ("credit_restrictions", 3286 credit_restrictions), 3287 GNUNET_JSON_pack_allow_null ( 3288 GNUNET_JSON_pack_string ("conversion_url", 3289 conversion_url)), 3290 GNUNET_JSON_pack_allow_null ( 3291 GNUNET_JSON_pack_string ("bank_label", 3292 bank_label)), 3293 GNUNET_JSON_pack_int64 ("priority", 3294 priority), 3295 GNUNET_JSON_pack_timestamp ("validity_start", 3296 now), 3297 GNUNET_JSON_pack_data_auto ("master_sig_add", 3298 &master_sig_add), 3299 GNUNET_JSON_pack_data_auto ("master_sig_wire", 3300 &master_sig_wire))); 3301 next (args + num_args); 3302 } 3303 3304 3305 /** 3306 * Disable wire account. 3307 * 3308 * @param args the array of command-line arguments to process next; 3309 * args[0] must be the payto URI of the account to disable 3310 */ 3311 static void 3312 do_del_wire (char *const *args) 3313 { 3314 struct TALER_MasterSignatureP master_sig; 3315 struct GNUNET_TIME_Timestamp now; 3316 struct TALER_FullPayto payto_uri = { 3317 .full_payto = args[0] 3318 }; 3319 3320 if (NULL != in) 3321 { 3322 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3323 "Downloaded data was not consumed, not deleting wire account\n") 3324 ; 3325 GNUNET_SCHEDULER_shutdown (); 3326 global_ret = EXIT_FAILURE; 3327 return; 3328 } 3329 if (NULL == args[0]) 3330 { 3331 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3332 "You must specify a payto://-URI with this subcommand\n"); 3333 GNUNET_SCHEDULER_shutdown (); 3334 global_ret = EXIT_INVALIDARGUMENT; 3335 return; 3336 } 3337 if (GNUNET_OK != 3338 load_offline_key (GNUNET_NO)) 3339 return; 3340 now = GNUNET_TIME_timestamp_get (); 3341 TALER_exchange_offline_wire_del_sign (payto_uri, 3342 now, 3343 &master_priv, 3344 &master_sig); 3345 output_operation (OP_DISABLE_WIRE, 3346 GNUNET_JSON_PACK ( 3347 TALER_JSON_pack_full_payto ("payto_uri", 3348 payto_uri), 3349 GNUNET_JSON_pack_timestamp ("validity_end", 3350 now), 3351 GNUNET_JSON_pack_data_auto ("master_sig", 3352 &master_sig))); 3353 next (args + 1); 3354 } 3355 3356 3357 /** 3358 * Set wire fees for the given year. 3359 * 3360 * @param args the array of command-line arguments to process next; 3361 * args[0] must be the year, args[1] the wire method, args[2] the wire fee and args[3] 3362 * the closing fee. 3363 */ 3364 static void 3365 do_set_wire_fee (char *const *args) 3366 { 3367 struct TALER_MasterSignatureP master_sig; 3368 char dummy; 3369 unsigned int year; 3370 struct TALER_WireFeeSet fees; 3371 struct GNUNET_TIME_Timestamp start_time; 3372 struct GNUNET_TIME_Timestamp end_time; 3373 3374 if (NULL != in) 3375 { 3376 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3377 "Downloaded data was not consumed, not setting wire fee\n"); 3378 GNUNET_SCHEDULER_shutdown (); 3379 global_ret = EXIT_FAILURE; 3380 return; 3381 } 3382 if ( (NULL == args[0]) || 3383 (NULL == args[1]) || 3384 (NULL == args[2]) || 3385 (NULL == args[3]) || 3386 ( (1 != sscanf (args[0], 3387 "%u%c", 3388 &year, 3389 &dummy)) && 3390 (0 != strcasecmp ("now", 3391 args[0])) ) || 3392 (GNUNET_OK != 3393 TALER_string_to_amount (args[2], 3394 &fees.wire)) || 3395 (GNUNET_OK != 3396 TALER_string_to_amount (args[3], 3397 &fees.closing)) ) 3398 { 3399 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3400 "You must use YEAR, METHOD, WIRE-FEE, and CLOSING-FEE as arguments for this subcommand\n"); 3401 GNUNET_SCHEDULER_shutdown (); 3402 global_ret = EXIT_INVALIDARGUMENT; 3403 return; 3404 } 3405 if (0 == strcasecmp ("now", 3406 args[0])) 3407 year = GNUNET_TIME_get_current_year (); 3408 if (GNUNET_OK != 3409 load_offline_key (GNUNET_NO)) 3410 return; 3411 start_time = GNUNET_TIME_absolute_to_timestamp ( 3412 GNUNET_TIME_year_to_time (year)); 3413 end_time = GNUNET_TIME_absolute_to_timestamp ( 3414 GNUNET_TIME_year_to_time (year + 1)); 3415 3416 TALER_exchange_offline_wire_fee_sign (args[1], 3417 start_time, 3418 end_time, 3419 &fees, 3420 &master_priv, 3421 &master_sig); 3422 output_operation (OP_SET_WIRE_FEE, 3423 GNUNET_JSON_PACK ( 3424 GNUNET_JSON_pack_string ("wire_method", 3425 args[1]), 3426 GNUNET_JSON_pack_timestamp ("start_time", 3427 start_time), 3428 GNUNET_JSON_pack_timestamp ("end_time", 3429 end_time), 3430 TALER_JSON_pack_amount ("wire_fee", 3431 &fees.wire), 3432 TALER_JSON_pack_amount ("closing_fee", 3433 &fees.closing), 3434 GNUNET_JSON_pack_data_auto ("master_sig", 3435 &master_sig))); 3436 next (args + 4); 3437 } 3438 3439 3440 /** 3441 * Set global fees for the given year. 3442 * 3443 * @param args the array of command-line arguments to process next; 3444 * args[0] must be the year, args[1] the history fee, args[2] 3445 * the account fee and args[3] the purse fee. These are followed by args[4] purse timeout, 3446 * args[5] history expiration. Last is args[6] the (free) purse account limit. 3447 */ 3448 static void 3449 do_set_global_fee (char *const *args) 3450 { 3451 struct TALER_MasterSignatureP master_sig; 3452 char dummy; 3453 unsigned int year; 3454 struct TALER_GlobalFeeSet fees; 3455 struct GNUNET_TIME_Relative purse_timeout; 3456 struct GNUNET_TIME_Relative history_expiration; 3457 unsigned int purse_account_limit; 3458 struct GNUNET_TIME_Timestamp start_time; 3459 struct GNUNET_TIME_Timestamp end_time; 3460 3461 if (NULL != in) 3462 { 3463 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3464 "Downloaded data was not consumed, not setting global fee\n"); 3465 GNUNET_SCHEDULER_shutdown (); 3466 global_ret = EXIT_FAILURE; 3467 return; 3468 } 3469 if ( (NULL == args[0]) || 3470 (NULL == args[1]) || 3471 (NULL == args[2]) || 3472 (NULL == args[3]) || 3473 (NULL == args[4]) || 3474 (NULL == args[5]) || 3475 (NULL == args[6]) ) 3476 { 3477 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3478 "You must use YEAR, HISTORY-FEE, ACCOUNT-FEE, PURSE-FEE, PURSE-TIMEOUT, HISTORY-EXPIRATION and PURSE-ACCOUNT-LIMIT as arguments for this subcommand\n"); 3479 GNUNET_SCHEDULER_shutdown (); 3480 global_ret = EXIT_INVALIDARGUMENT; 3481 return; 3482 } 3483 if ( (1 != sscanf (args[0], 3484 "%u%c", 3485 &year, 3486 &dummy)) && 3487 (0 != strcasecmp ("now", 3488 args[0])) ) 3489 { 3490 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3491 "Invalid YEAR given for 'global-fee' subcommand\n"); 3492 GNUNET_SCHEDULER_shutdown (); 3493 global_ret = EXIT_INVALIDARGUMENT; 3494 return; 3495 } 3496 if ( (GNUNET_OK != 3497 TALER_string_to_amount (args[1], 3498 &fees.history)) || 3499 (GNUNET_OK != 3500 TALER_string_to_amount (args[2], 3501 &fees.account)) || 3502 (GNUNET_OK != 3503 TALER_string_to_amount (args[3], 3504 &fees.purse)) ) 3505 { 3506 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3507 "Invalid amount given for 'global-fee' subcommand\n"); 3508 GNUNET_SCHEDULER_shutdown (); 3509 global_ret = EXIT_INVALIDARGUMENT; 3510 return; 3511 } 3512 if (GNUNET_OK != 3513 GNUNET_STRINGS_fancy_time_to_relative (args[4], 3514 &purse_timeout)) 3515 { 3516 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3517 "Invalid purse timeout `%s' given for 'global-fee' subcommand\n", 3518 args[4]); 3519 GNUNET_SCHEDULER_shutdown (); 3520 global_ret = EXIT_INVALIDARGUMENT; 3521 return; 3522 } 3523 if (GNUNET_OK != 3524 GNUNET_STRINGS_fancy_time_to_relative (args[5], 3525 &history_expiration)) 3526 { 3527 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3528 "Invalid history expiratoin `%s' given for 'global-fee' subcommand\n", 3529 args[5]); 3530 GNUNET_SCHEDULER_shutdown (); 3531 global_ret = EXIT_INVALIDARGUMENT; 3532 return; 3533 } 3534 if (1 != sscanf (args[6], 3535 "%u%c", 3536 &purse_account_limit, 3537 &dummy)) 3538 { 3539 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3540 "Invalid purse account limit given for 'global-fee' subcommand\n"); 3541 GNUNET_SCHEDULER_shutdown (); 3542 global_ret = EXIT_INVALIDARGUMENT; 3543 return; 3544 } 3545 if (0 == strcasecmp ("now", 3546 args[0])) 3547 year = GNUNET_TIME_get_current_year (); 3548 if (GNUNET_OK != 3549 load_offline_key (GNUNET_NO)) 3550 return; 3551 start_time = GNUNET_TIME_absolute_to_timestamp ( 3552 GNUNET_TIME_year_to_time (year)); 3553 end_time = GNUNET_TIME_absolute_to_timestamp ( 3554 GNUNET_TIME_year_to_time (year + 1)); 3555 3556 TALER_exchange_offline_global_fee_sign (start_time, 3557 end_time, 3558 &fees, 3559 purse_timeout, 3560 history_expiration, 3561 (uint32_t) purse_account_limit, 3562 &master_priv, 3563 &master_sig); 3564 output_operation (OP_SET_GLOBAL_FEE, 3565 GNUNET_JSON_PACK ( 3566 GNUNET_JSON_pack_timestamp ("start_time", 3567 start_time), 3568 GNUNET_JSON_pack_timestamp ("end_time", 3569 end_time), 3570 TALER_JSON_pack_amount ("history_fee", 3571 &fees.history), 3572 TALER_JSON_pack_amount ("account_fee", 3573 &fees.account), 3574 TALER_JSON_pack_amount ("purse_fee", 3575 &fees.purse), 3576 GNUNET_JSON_pack_time_rel ("purse_timeout", 3577 purse_timeout), 3578 GNUNET_JSON_pack_time_rel ("history_expiration", 3579 history_expiration), 3580 GNUNET_JSON_pack_uint64 ("purse_account_limit", 3581 (uint32_t) purse_account_limit), 3582 GNUNET_JSON_pack_data_auto ("master_sig", 3583 &master_sig))); 3584 next (args + 7); 3585 } 3586 3587 3588 /** 3589 * Drain profits from exchange's escrow account to 3590 * regular exchange account. 3591 * 3592 * @param args the array of command-line arguments to process next; 3593 * args[0] must be the amount, 3594 * args[1] must be the section of the escrow account to drain 3595 * args[2] must be the payto://-URI of the target account 3596 */ 3597 static void 3598 do_drain (char *const *args) 3599 { 3600 struct TALER_WireTransferIdentifierRawP wtid; 3601 struct GNUNET_TIME_Timestamp date; 3602 struct TALER_Amount amount; 3603 const char *account_section; 3604 struct TALER_FullPayto payto_uri; 3605 struct TALER_MasterSignatureP master_sig; 3606 char *err; 3607 3608 if (NULL != in) 3609 { 3610 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3611 "Downloaded data was not consumed, refusing drain\n"); 3612 GNUNET_SCHEDULER_shutdown (); 3613 global_ret = EXIT_FAILURE; 3614 return; 3615 } 3616 if ( (NULL == args[0]) || 3617 (NULL == args[1]) || 3618 (NULL == args[2]) || 3619 (GNUNET_OK != 3620 TALER_string_to_amount (args[0], 3621 &amount)) ) 3622 { 3623 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3624 "Drain requires an amount, section name and target payto://-URI as arguments\n"); 3625 GNUNET_SCHEDULER_shutdown (); 3626 global_ret = EXIT_INVALIDARGUMENT; 3627 return; 3628 } 3629 if ( (NULL == args[0]) || 3630 (NULL == args[1]) || 3631 (NULL == args[2]) ) 3632 { 3633 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3634 "Drain requires an amount, section name and target payto://-URI as arguments\n"); 3635 GNUNET_SCHEDULER_shutdown (); 3636 global_ret = EXIT_INVALIDARGUMENT; 3637 return; 3638 } 3639 if (GNUNET_OK != 3640 TALER_string_to_amount (args[0], 3641 &amount)) 3642 { 3643 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3644 "Invalid amount `%s' specified for drain\n", 3645 args[0]); 3646 GNUNET_SCHEDULER_shutdown (); 3647 global_ret = EXIT_INVALIDARGUMENT; 3648 return; 3649 } 3650 account_section = args[1]; 3651 payto_uri.full_payto = args[2]; 3652 err = TALER_payto_validate (payto_uri); 3653 if (NULL != err) 3654 { 3655 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3656 "Invalid payto://-URI `%s' specified for drain: %s\n", 3657 payto_uri.full_payto, 3658 err); 3659 GNUNET_free (err); 3660 GNUNET_SCHEDULER_shutdown (); 3661 global_ret = EXIT_INVALIDARGUMENT; 3662 return; 3663 } 3664 if (GNUNET_OK != 3665 load_offline_key (GNUNET_NO)) 3666 return; 3667 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 3668 &wtid, 3669 sizeof (wtid)); 3670 date = GNUNET_TIME_timestamp_get (); 3671 TALER_exchange_offline_profit_drain_sign (&wtid, 3672 date, 3673 &amount, 3674 account_section, 3675 payto_uri, 3676 &master_priv, 3677 &master_sig); 3678 output_operation (OP_DRAIN_PROFITS, 3679 GNUNET_JSON_PACK ( 3680 GNUNET_JSON_pack_data_auto ("wtid", 3681 &wtid), 3682 GNUNET_JSON_pack_string ("account_section", 3683 account_section), 3684 TALER_JSON_pack_full_payto ("payto_uri", 3685 payto_uri), 3686 TALER_JSON_pack_amount ("amount", 3687 &amount), 3688 GNUNET_JSON_pack_timestamp ("date", 3689 date), 3690 GNUNET_JSON_pack_data_auto ("master_sig", 3691 &master_sig))); 3692 next (args + 3); 3693 } 3694 3695 3696 /** 3697 * Add partner. 3698 * 3699 * @param args the array of command-line arguments to process next; 3700 * args[0] must be the partner's master public key, args[1] the partner's 3701 * API base URL, args[2] the wad fee, args[3] the wad frequency, and 3702 * args[4] the year (including possibly 'now') 3703 */ 3704 static void 3705 do_add_partner (char *const *args) 3706 { 3707 struct TALER_MasterPublicKeyP partner_pub; 3708 struct GNUNET_TIME_Timestamp start_date; 3709 struct GNUNET_TIME_Timestamp end_date; 3710 struct GNUNET_TIME_Relative wad_frequency; 3711 struct TALER_Amount wad_fee; 3712 const char *partner_base_url; 3713 struct TALER_MasterSignatureP master_sig; 3714 char dummy; 3715 unsigned int year; 3716 3717 if (NULL != in) 3718 { 3719 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3720 "Downloaded data was not consumed, not adding partner\n"); 3721 GNUNET_SCHEDULER_shutdown (); 3722 global_ret = EXIT_FAILURE; 3723 return; 3724 } 3725 if ( (NULL == args[0]) || 3726 (GNUNET_OK != 3727 GNUNET_STRINGS_string_to_data (args[0], 3728 strlen (args[0]), 3729 &partner_pub, 3730 sizeof (partner_pub))) ) 3731 { 3732 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3733 "You must specify the partner master public key as first argument for this subcommand\n"); 3734 GNUNET_SCHEDULER_shutdown (); 3735 global_ret = EXIT_INVALIDARGUMENT; 3736 return; 3737 } 3738 if ( (NULL == args[1]) || 3739 (0 != strncmp ("http", 3740 args[1], 3741 strlen ("http"))) ) 3742 { 3743 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3744 "You must specify the partner's base URL as the 2nd argument to this subcommand\n"); 3745 GNUNET_SCHEDULER_shutdown (); 3746 global_ret = EXIT_INVALIDARGUMENT; 3747 return; 3748 } 3749 partner_base_url = args[1]; 3750 if ( (NULL == args[2]) || 3751 (GNUNET_OK != 3752 TALER_string_to_amount (args[2], 3753 &wad_fee)) ) 3754 { 3755 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3756 "Invalid amount `%s' specified for wad fee of partner\n", 3757 args[2]); 3758 GNUNET_SCHEDULER_shutdown (); 3759 global_ret = EXIT_INVALIDARGUMENT; 3760 return; 3761 } 3762 if ( (NULL == args[3]) || 3763 (GNUNET_OK != 3764 GNUNET_STRINGS_fancy_time_to_relative (args[3], 3765 &wad_frequency)) ) 3766 { 3767 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3768 "Invalid wad frequency `%s' specified for add partner\n", 3769 args[3]); 3770 GNUNET_SCHEDULER_shutdown (); 3771 global_ret = EXIT_INVALIDARGUMENT; 3772 return; 3773 } 3774 if ( (NULL == args[4]) || 3775 ( (1 != sscanf (args[4], 3776 "%u%c", 3777 &year, 3778 &dummy)) && 3779 (0 != strcasecmp ("now", 3780 args[4])) ) ) 3781 { 3782 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3783 "Invalid year `%s' specified for add partner\n", 3784 args[4]); 3785 GNUNET_SCHEDULER_shutdown (); 3786 global_ret = EXIT_INVALIDARGUMENT; 3787 return; 3788 } 3789 if (0 == strcasecmp ("now", 3790 args[4])) 3791 year = GNUNET_TIME_get_current_year (); 3792 start_date = GNUNET_TIME_absolute_to_timestamp ( 3793 GNUNET_TIME_year_to_time (year)); 3794 end_date = GNUNET_TIME_absolute_to_timestamp ( 3795 GNUNET_TIME_year_to_time (year + 1)); 3796 3797 if (GNUNET_OK != 3798 load_offline_key (GNUNET_NO)) 3799 return; 3800 TALER_exchange_offline_partner_details_sign (&partner_pub, 3801 start_date, 3802 end_date, 3803 wad_frequency, 3804 &wad_fee, 3805 partner_base_url, 3806 &master_priv, 3807 &master_sig); 3808 output_operation (OP_ADD_PARTNER, 3809 GNUNET_JSON_PACK ( 3810 GNUNET_JSON_pack_string ("partner_base_url", 3811 partner_base_url), 3812 GNUNET_JSON_pack_time_rel ("wad_frequency", 3813 wad_frequency), 3814 GNUNET_JSON_pack_timestamp ("start_date", 3815 start_date), 3816 GNUNET_JSON_pack_timestamp ("end_date", 3817 end_date), 3818 GNUNET_JSON_pack_data_auto ("partner_pub", 3819 &partner_pub), 3820 GNUNET_JSON_pack_data_auto ("master_sig", 3821 &master_sig))); 3822 next (args + 5); 3823 } 3824 3825 3826 /** 3827 * Enable or disable AML staff. 3828 * 3829 * @param is_active true to enable, false to disable 3830 * @param args the array of command-line arguments to process next; args[0] must be the AML staff's public key, args[1] the AML staff's legal name, and if @a is_active then args[2] rw (read write) or ro (read only) 3831 */ 3832 static void 3833 do_set_aml_staff (bool is_active, 3834 char *const *args) 3835 { 3836 struct TALER_AmlOfficerPublicKeyP officer_pub; 3837 const char *officer_name; 3838 bool read_only; 3839 struct TALER_MasterSignatureP master_sig; 3840 struct GNUNET_TIME_Timestamp now 3841 = GNUNET_TIME_timestamp_get (); 3842 3843 if (NULL != in) 3844 { 3845 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3846 "Downloaded data was not consumed, not updating AML staff status\n"); 3847 GNUNET_SCHEDULER_shutdown (); 3848 global_ret = EXIT_FAILURE; 3849 return; 3850 } 3851 if ( (NULL == args[0]) || 3852 (GNUNET_OK != 3853 GNUNET_STRINGS_string_to_data (args[0], 3854 strlen (args[0]), 3855 &officer_pub, 3856 sizeof (officer_pub))) ) 3857 { 3858 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3859 "You must specify the AML officer's public key as first argument for this subcommand\n"); 3860 GNUNET_SCHEDULER_shutdown (); 3861 global_ret = EXIT_INVALIDARGUMENT; 3862 return; 3863 } 3864 if (NULL == args[1]) 3865 { 3866 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3867 "You must specify the officer's legal name as the 2nd argument to this subcommand\n"); 3868 GNUNET_SCHEDULER_shutdown (); 3869 global_ret = EXIT_INVALIDARGUMENT; 3870 return; 3871 } 3872 officer_name = args[1]; 3873 if (is_active) 3874 { 3875 if ( (NULL == args[2]) || 3876 ( (0 != strcmp (args[2], 3877 "ro")) && 3878 (0 != strcmp (args[2], 3879 "rw")) ) ) 3880 { 3881 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3882 "You must specify 'ro' or 'rw' (and not `%s') for the access level\n", 3883 args[2]); 3884 GNUNET_SCHEDULER_shutdown (); 3885 global_ret = EXIT_INVALIDARGUMENT; 3886 return; 3887 } 3888 read_only = (0 == strcmp (args[2], 3889 "ro")); 3890 } 3891 else 3892 { 3893 read_only = true; 3894 } 3895 if (GNUNET_OK != 3896 load_offline_key (GNUNET_NO)) 3897 return; 3898 TALER_exchange_offline_aml_officer_status_sign (&officer_pub, 3899 officer_name, 3900 now, 3901 is_active, 3902 read_only, 3903 &master_priv, 3904 &master_sig); 3905 output_operation (OP_UPDATE_AML_STAFF, 3906 GNUNET_JSON_PACK ( 3907 GNUNET_JSON_pack_string ("officer_name", 3908 officer_name), 3909 GNUNET_JSON_pack_timestamp ("change_date", 3910 now), 3911 GNUNET_JSON_pack_bool ("is_active", 3912 is_active), 3913 GNUNET_JSON_pack_bool ("read_only", 3914 read_only), 3915 GNUNET_JSON_pack_data_auto ("officer_pub", 3916 &officer_pub), 3917 GNUNET_JSON_pack_data_auto ("master_sig", 3918 &master_sig))); 3919 next (args + (is_active ? 3 : 2)); 3920 } 3921 3922 3923 /** 3924 * Disable AML staff. 3925 * 3926 * @param args the array of command-line arguments to process next; 3927 * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) 3928 */ 3929 static void 3930 disable_aml_staff (char *const *args) 3931 { 3932 do_set_aml_staff (false, 3933 args); 3934 } 3935 3936 3937 /** 3938 * Enable AML staff. 3939 * 3940 * @param args the array of command-line arguments to process next; 3941 * args[0] must be the AML staff's public key, args[1] the AML staff's legal name, args[2] rw (read write) or ro (read only) 3942 */ 3943 static void 3944 enable_aml_staff (char *const *args) 3945 { 3946 do_set_aml_staff (true, 3947 args); 3948 } 3949 3950 3951 /** 3952 * Function called with information about future keys. Dumps the JSON output 3953 * (on success), either into an internal buffer or to stdout (depending on 3954 * whether there are subsequent commands). 3955 * 3956 * @param cls closure with the `char **` remaining args 3957 * @param mgr response data 3958 */ 3959 static void 3960 download_cb (void *cls, 3961 const struct TALER_EXCHANGE_ManagementGetKeysResponse *mgr) 3962 { 3963 char *const *args = cls; 3964 const struct TALER_EXCHANGE_HttpResponse *hr = &mgr->hr; 3965 3966 mgkh = NULL; 3967 switch (hr->http_status) 3968 { 3969 case MHD_HTTP_OK: 3970 break; 3971 default: 3972 if (0 != hr->http_status) 3973 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3974 "Failed to download keys from `%s': %s (HTTP status: %u/%u)\n", 3975 CFG_exchange_url, 3976 hr->hint, 3977 hr->http_status, 3978 (unsigned int) hr->ec); 3979 else 3980 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 3981 "Failed to download keys from `%s' (no HTTP response)\n", 3982 CFG_exchange_url); 3983 GNUNET_SCHEDULER_shutdown (); 3984 global_ret = EXIT_FAILURE; 3985 return; 3986 } 3987 in = GNUNET_JSON_PACK ( 3988 GNUNET_JSON_pack_string ("operation", 3989 OP_INPUT_KEYS), 3990 GNUNET_JSON_pack_object_incref ("arguments", 3991 (json_t *) hr->reply)); 3992 if (NULL == args[0]) 3993 { 3994 json_dumpf (in, 3995 stdout, 3996 JSON_INDENT (2)); 3997 json_decref (in); 3998 in = NULL; 3999 } 4000 next (args); 4001 } 4002 4003 4004 /** 4005 * Download future keys. 4006 * 4007 * @param args the array of command-line arguments to process next 4008 */ 4009 static void 4010 do_download (char *const *args) 4011 { 4012 if ( (NULL == CFG_exchange_url) && 4013 (GNUNET_OK != 4014 GNUNET_CONFIGURATION_get_value_string (kcfg, 4015 "exchange", 4016 "BASE_URL", 4017 &CFG_exchange_url)) ) 4018 { 4019 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 4020 "exchange", 4021 "BASE_URL"); 4022 GNUNET_SCHEDULER_shutdown (); 4023 global_ret = EXIT_NOTCONFIGURED; 4024 return; 4025 } 4026 mgkh = TALER_EXCHANGE_get_management_keys (ctx, 4027 CFG_exchange_url, 4028 &download_cb, 4029 (void *) args); 4030 } 4031 4032 4033 /** 4034 * Check that the security module keys are the same as before. If we had no 4035 * keys in store before, remember them (Trust On First Use). 4036 * 4037 * @param secmset security module keys 4038 * @return #GNUNET_OK if keys match with what we have in store 4039 * #GNUNET_NO if we had nothing in store but now do 4040 * #GNUNET_SYSERR if keys changed from what we remember or other error 4041 */ 4042 static enum GNUNET_GenericReturnValue 4043 tofu_check (const struct TALER_SecurityModulePublicKeySetP *secmset) 4044 { 4045 char *fn; 4046 struct TALER_SecurityModulePublicKeySetP oldset; 4047 ssize_t ret; 4048 4049 if (GNUNET_OK != 4050 GNUNET_CONFIGURATION_get_value_filename (kcfg, 4051 "exchange-offline", 4052 "SECM_TOFU_FILE", 4053 &fn)) 4054 { 4055 GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR, 4056 "exchange-offline", 4057 "SECM_TOFU_FILE"); 4058 return GNUNET_SYSERR; 4059 } 4060 if (GNUNET_OK == 4061 GNUNET_DISK_file_test (fn)) 4062 { 4063 ret = GNUNET_DISK_fn_read (fn, 4064 &oldset, 4065 sizeof (oldset)); 4066 if (GNUNET_SYSERR != ret) 4067 { 4068 if (ret != sizeof (oldset)) 4069 { 4070 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4071 "File `%s' corrupt\n", 4072 fn); 4073 GNUNET_free (fn); 4074 return GNUNET_SYSERR; 4075 } 4076 /* TOFU check */ 4077 if (0 != memcmp (&oldset, 4078 secmset, 4079 sizeof (*secmset))) 4080 { 4081 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4082 "Fatal: security module keys changed (file `%s')!\n", 4083 fn); 4084 GNUNET_free (fn); 4085 return GNUNET_SYSERR; 4086 } 4087 GNUNET_free (fn); 4088 return GNUNET_OK; 4089 } 4090 } 4091 4092 { 4093 char *key; 4094 4095 /* check against SECMOD-keys pinned in configuration */ 4096 if (GNUNET_OK == 4097 GNUNET_CONFIGURATION_get_value_string (kcfg, 4098 "exchange-offline", 4099 "SECM_ESIGN_PUBKEY", 4100 &key)) 4101 { 4102 struct TALER_SecurityModulePublicKeyP k; 4103 4104 if (GNUNET_OK != 4105 GNUNET_STRINGS_string_to_data (key, 4106 strlen (key), 4107 &k, 4108 sizeof (k))) 4109 { 4110 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4111 "exchange-offline", 4112 "SECM_ESIGN_PUBKEY", 4113 "key malformed"); 4114 GNUNET_free (key); 4115 GNUNET_free (fn); 4116 return GNUNET_SYSERR; 4117 } 4118 GNUNET_free (key); 4119 if (0 != 4120 GNUNET_memcmp (&k, 4121 &secmset->eddsa)) 4122 { 4123 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4124 "ESIGN security module key does not match SECM_ESIGN_PUBKEY in configuration\n"); 4125 GNUNET_free (fn); 4126 return GNUNET_SYSERR; 4127 } 4128 } 4129 if (GNUNET_OK == 4130 GNUNET_CONFIGURATION_get_value_string (kcfg, 4131 "exchange-offline", 4132 "SECM_DENOM_PUBKEY", 4133 &key)) 4134 { 4135 struct TALER_SecurityModulePublicKeyP k; 4136 4137 if (GNUNET_OK != 4138 GNUNET_STRINGS_string_to_data (key, 4139 strlen (key), 4140 &k, 4141 sizeof (k))) 4142 { 4143 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4144 "exchange-offline", 4145 "SECM_DENOM_PUBKEY", 4146 "key malformed"); 4147 GNUNET_free (key); 4148 GNUNET_free (fn); 4149 return GNUNET_SYSERR; 4150 } 4151 GNUNET_free (key); 4152 if (0 != 4153 GNUNET_memcmp (&k, 4154 &secmset->rsa)) 4155 { 4156 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4157 "DENOM security module key does not match SECM_DENOM_PUBKEY in configuration\n"); 4158 GNUNET_free (fn); 4159 return GNUNET_SYSERR; 4160 } 4161 } 4162 if (GNUNET_OK == 4163 GNUNET_CONFIGURATION_get_value_string (kcfg, 4164 "exchange-offline", 4165 "SECM_DENOM_CS_PUBKEY", 4166 &key)) 4167 { 4168 struct TALER_SecurityModulePublicKeyP k; 4169 4170 if (GNUNET_OK != 4171 GNUNET_STRINGS_string_to_data (key, 4172 strlen (key), 4173 &k, 4174 sizeof (k))) 4175 { 4176 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4177 "exchange-offline", 4178 "SECM_DENOM_CS_PUBKEY", 4179 "key malformed"); 4180 GNUNET_free (key); 4181 GNUNET_free (fn); 4182 return GNUNET_SYSERR; 4183 } 4184 GNUNET_free (key); 4185 if (0 != 4186 GNUNET_memcmp (&k, 4187 &secmset->cs)) 4188 { 4189 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4190 "DENOM security module key does not match SECM_DENOM_CS_PUBKEY in configuration\n"); 4191 GNUNET_free (fn); 4192 return GNUNET_SYSERR; 4193 } 4194 } 4195 } 4196 if (GNUNET_OK != 4197 GNUNET_DISK_directory_create_for_file (fn)) 4198 { 4199 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4200 "Failed create directory to store key material in file `%s'\n", 4201 fn); 4202 GNUNET_free (fn); 4203 return GNUNET_SYSERR; 4204 } 4205 /* persist keys for future runs */ 4206 if (GNUNET_OK != 4207 GNUNET_DISK_fn_write (fn, 4208 secmset, 4209 sizeof (oldset), 4210 GNUNET_DISK_PERM_USER_READ)) 4211 { 4212 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4213 "Failed to store key material in file `%s'\n", 4214 fn); 4215 GNUNET_free (fn); 4216 return GNUNET_SYSERR; 4217 } 4218 GNUNET_free (fn); 4219 return GNUNET_NO; 4220 } 4221 4222 4223 /** 4224 * Output @a signkeys for human consumption. 4225 * 4226 * @param secm_pub security module public key used to sign the denominations 4227 * @param signkeys keys to output 4228 * @return #GNUNET_OK on success 4229 */ 4230 static enum GNUNET_GenericReturnValue 4231 show_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4232 const json_t *signkeys) 4233 { 4234 size_t index; 4235 json_t *value; 4236 4237 json_array_foreach (signkeys, index, value) { 4238 const char *err_name; 4239 unsigned int err_line; 4240 struct TALER_ExchangePublicKeyP exchange_pub; 4241 struct TALER_SecurityModuleSignatureP secm_sig; 4242 struct GNUNET_TIME_Timestamp start_time; 4243 struct GNUNET_TIME_Timestamp sign_end; 4244 struct GNUNET_TIME_Timestamp legal_end; 4245 struct GNUNET_TIME_Relative duration; 4246 struct GNUNET_JSON_Specification spec[] = { 4247 GNUNET_JSON_spec_timestamp ("stamp_start", 4248 &start_time), 4249 GNUNET_JSON_spec_timestamp ("stamp_expire", 4250 &sign_end), 4251 GNUNET_JSON_spec_timestamp ("stamp_end", 4252 &legal_end), 4253 GNUNET_JSON_spec_fixed_auto ("key", 4254 &exchange_pub), 4255 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4256 &secm_sig), 4257 GNUNET_JSON_spec_end () 4258 }; 4259 4260 if (GNUNET_OK != 4261 GNUNET_JSON_parse (value, 4262 spec, 4263 &err_name, 4264 &err_line)) 4265 { 4266 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4267 "Invalid input for signing key to 'show': %s#%u at %u (skipping)\n", 4268 err_name, 4269 err_line, 4270 (unsigned int) index); 4271 json_dumpf (value, 4272 stderr, 4273 JSON_INDENT (2)); 4274 global_ret = EXIT_FAILURE; 4275 GNUNET_SCHEDULER_shutdown (); 4276 return GNUNET_SYSERR; 4277 } 4278 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4279 sign_end.abs_time); 4280 if (GNUNET_OK != 4281 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4282 start_time, 4283 duration, 4284 secm_pub, 4285 &secm_sig)) 4286 { 4287 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4288 "Invalid security module signature for signing key %s (aborting)\n", 4289 TALER_B2S (&exchange_pub)); 4290 global_ret = EXIT_FAILURE; 4291 GNUNET_SCHEDULER_shutdown (); 4292 return GNUNET_SYSERR; 4293 } 4294 { 4295 char *legal_end_s; 4296 4297 legal_end_s = GNUNET_strdup ( 4298 GNUNET_TIME_timestamp2s (legal_end)); 4299 printf ("EXCHANGE-KEY %s starting at %s (used for: %s, legal end: %s)\n", 4300 TALER_B2S (&exchange_pub), 4301 GNUNET_TIME_timestamp2s (start_time), 4302 GNUNET_TIME_relative2s (duration, 4303 false), 4304 legal_end_s); 4305 GNUNET_free (legal_end_s); 4306 } 4307 } 4308 return GNUNET_OK; 4309 } 4310 4311 4312 /** 4313 * Output @a denomkeys for human consumption. 4314 * 4315 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4316 * @param secm_pub_cs security module public key used to sign the CS denominations 4317 * @param denomkeys keys to output 4318 * @return #GNUNET_OK on success 4319 */ 4320 static enum GNUNET_GenericReturnValue 4321 show_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4322 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4323 const json_t *denomkeys) 4324 { 4325 size_t index; 4326 json_t *value; 4327 4328 json_array_foreach (denomkeys, index, value) { 4329 const char *err_name; 4330 unsigned int err_line; 4331 const char *section_name; 4332 struct TALER_DenominationPublicKey denom_pub; 4333 struct GNUNET_TIME_Timestamp stamp_start; 4334 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4335 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4336 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4337 struct TALER_Amount coin_value; 4338 struct TALER_DenomFeeSet fees; 4339 struct TALER_SecurityModuleSignatureP secm_sig; 4340 struct GNUNET_JSON_Specification spec[] = { 4341 GNUNET_JSON_spec_string ("section_name", 4342 §ion_name), 4343 TALER_JSON_spec_denom_pub ("denom_pub", 4344 &denom_pub), 4345 TALER_JSON_spec_amount ("value", 4346 currency, 4347 &coin_value), 4348 TALER_JSON_SPEC_DENOM_FEES ("fee", 4349 currency, 4350 &fees), 4351 GNUNET_JSON_spec_timestamp ("stamp_start", 4352 &stamp_start), 4353 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4354 &stamp_expire_withdraw), 4355 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4356 &stamp_expire_deposit), 4357 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4358 &stamp_expire_legal), 4359 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4360 &secm_sig), 4361 GNUNET_JSON_spec_end () 4362 }; 4363 struct GNUNET_TIME_Relative duration; 4364 struct TALER_DenominationHashP h_denom_pub; 4365 enum GNUNET_GenericReturnValue ok; 4366 4367 if (GNUNET_OK != 4368 GNUNET_JSON_parse (value, 4369 spec, 4370 &err_name, 4371 &err_line)) 4372 { 4373 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4374 "Invalid input for denomination key to 'show': %s#%u at %u (skipping)\n", 4375 err_name, 4376 err_line, 4377 (unsigned int) index); 4378 json_dumpf (value, 4379 stderr, 4380 JSON_INDENT (2)); 4381 GNUNET_JSON_parse_free (spec); 4382 global_ret = EXIT_FAILURE; 4383 GNUNET_SCHEDULER_shutdown (); 4384 return GNUNET_SYSERR; 4385 } 4386 duration = GNUNET_TIME_absolute_get_difference ( 4387 stamp_start.abs_time, 4388 stamp_expire_withdraw.abs_time); 4389 TALER_denom_pub_hash (&denom_pub, 4390 &h_denom_pub); 4391 switch (denom_pub.bsign_pub_key->cipher) 4392 { 4393 case GNUNET_CRYPTO_BSA_RSA: 4394 { 4395 struct TALER_RsaPubHashP h_rsa; 4396 4397 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4398 &h_rsa); 4399 ok = TALER_exchange_secmod_rsa_verify (&h_rsa, 4400 section_name, 4401 stamp_start, 4402 duration, 4403 secm_pub_rsa, 4404 &secm_sig); 4405 } 4406 break; 4407 case GNUNET_CRYPTO_BSA_CS: 4408 { 4409 struct TALER_CsPubHashP h_cs; 4410 4411 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4412 &h_cs); 4413 ok = TALER_exchange_secmod_cs_verify (&h_cs, 4414 section_name, 4415 stamp_start, 4416 duration, 4417 secm_pub_cs, 4418 &secm_sig); 4419 } 4420 break; 4421 default: 4422 GNUNET_break (0); 4423 ok = GNUNET_SYSERR; 4424 break; 4425 } 4426 if (GNUNET_OK != ok) 4427 { 4428 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4429 "Invalid security module signature for denomination key %s (aborting)\n", 4430 GNUNET_h2s (&h_denom_pub.hash)); 4431 global_ret = EXIT_FAILURE; 4432 GNUNET_SCHEDULER_shutdown (); 4433 return GNUNET_SYSERR; 4434 } 4435 4436 { 4437 char *withdraw_fee_s; 4438 char *deposit_fee_s; 4439 char *refresh_fee_s; 4440 char *refund_fee_s; 4441 char *deposit_s; 4442 char *legal_s; 4443 4444 withdraw_fee_s = TALER_amount_to_string (&fees.withdraw); 4445 deposit_fee_s = TALER_amount_to_string (&fees.deposit); 4446 refresh_fee_s = TALER_amount_to_string (&fees.refresh); 4447 refund_fee_s = TALER_amount_to_string (&fees.refund); 4448 deposit_s = GNUNET_strdup ( 4449 GNUNET_TIME_timestamp2s (stamp_expire_deposit)); 4450 legal_s = GNUNET_strdup ( 4451 GNUNET_TIME_timestamp2s (stamp_expire_legal)); 4452 4453 printf ( 4454 "DENOMINATION-KEY(%s) %s of value %s starting at %s " 4455 "(used for: %s, deposit until: %s legal end: %s) with fees %s/%s/%s/%s\n", 4456 section_name, 4457 TALER_B2S (&h_denom_pub), 4458 TALER_amount2s (&coin_value), 4459 GNUNET_TIME_timestamp2s (stamp_start), 4460 GNUNET_TIME_relative2s (duration, 4461 false), 4462 deposit_s, 4463 legal_s, 4464 withdraw_fee_s, 4465 deposit_fee_s, 4466 refresh_fee_s, 4467 refund_fee_s); 4468 GNUNET_free (withdraw_fee_s); 4469 GNUNET_free (deposit_fee_s); 4470 GNUNET_free (refresh_fee_s); 4471 GNUNET_free (refund_fee_s); 4472 GNUNET_free (deposit_s); 4473 GNUNET_free (legal_s); 4474 } 4475 4476 GNUNET_JSON_parse_free (spec); 4477 } 4478 return GNUNET_OK; 4479 } 4480 4481 4482 /** 4483 * Parse the input of exchange keys for the 'show' and 'sign' commands. 4484 * 4485 * @param command_name name of the command, for logging 4486 * @return NULL on error, otherwise the keys details to be free'd by caller 4487 */ 4488 static json_t * 4489 parse_keys_input (const char *command_name) 4490 { 4491 const char *op_str; 4492 json_t *keys; 4493 struct GNUNET_JSON_Specification spec[] = { 4494 GNUNET_JSON_spec_json ("arguments", 4495 &keys), 4496 GNUNET_JSON_spec_string ("operation", 4497 &op_str), 4498 GNUNET_JSON_spec_end () 4499 }; 4500 const char *err_name; 4501 unsigned int err_line; 4502 4503 if (NULL == in) 4504 { 4505 json_error_t err; 4506 4507 in = json_loadf (stdin, 4508 JSON_REJECT_DUPLICATES, 4509 &err); 4510 if (NULL == in) 4511 { 4512 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4513 "Failed to read JSON input: %s at %d:%s (offset: %d)\n", 4514 err.text, 4515 err.line, 4516 err.source, 4517 err.position); 4518 global_ret = EXIT_FAILURE; 4519 GNUNET_SCHEDULER_shutdown (); 4520 return NULL; 4521 } 4522 } 4523 if (GNUNET_OK != 4524 GNUNET_JSON_parse (in, 4525 spec, 4526 &err_name, 4527 &err_line)) 4528 { 4529 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4530 "Invalid input to '%s': %s#%u (skipping)\n", 4531 command_name, 4532 err_name, 4533 err_line); 4534 json_dumpf (in, 4535 stderr, 4536 JSON_INDENT (2)); 4537 global_ret = EXIT_FAILURE; 4538 GNUNET_SCHEDULER_shutdown (); 4539 return NULL; 4540 } 4541 if (0 != strcmp (op_str, 4542 OP_INPUT_KEYS)) 4543 { 4544 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4545 "Invalid input to '%s' : operation is `%s', expected `%s'\n", 4546 command_name, 4547 op_str, 4548 OP_INPUT_KEYS); 4549 GNUNET_JSON_parse_free (spec); 4550 return NULL; 4551 } 4552 json_decref (in); 4553 in = NULL; 4554 return keys; 4555 } 4556 4557 4558 /** 4559 * Show future keys. 4560 * 4561 * @param args the array of command-line arguments to process next 4562 */ 4563 static void 4564 do_show (char *const *args) 4565 { 4566 json_t *keys; 4567 const char *err_name; 4568 unsigned int err_line; 4569 const json_t *denomkeys; 4570 const json_t *signkeys; 4571 struct TALER_MasterPublicKeyP mpub; 4572 struct TALER_SecurityModulePublicKeySetP secmset; 4573 struct GNUNET_JSON_Specification spec[] = { 4574 GNUNET_JSON_spec_array_const ("future_denoms", 4575 &denomkeys), 4576 GNUNET_JSON_spec_array_const ("future_signkeys", 4577 &signkeys), 4578 GNUNET_JSON_spec_fixed_auto ("master_pub", 4579 &mpub), 4580 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4581 &secmset.rsa), 4582 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4583 &secmset.cs), 4584 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4585 &secmset.eddsa), 4586 GNUNET_JSON_spec_end () 4587 }; 4588 4589 keys = parse_keys_input ("show"); 4590 if (NULL == keys) 4591 return; 4592 4593 if (GNUNET_OK != 4594 load_offline_key (GNUNET_NO)) 4595 return; 4596 if (GNUNET_OK != 4597 GNUNET_JSON_parse (keys, 4598 spec, 4599 &err_name, 4600 &err_line)) 4601 { 4602 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4603 "Invalid input to 'show': %s #%u (skipping)\n", 4604 err_name, 4605 err_line); 4606 json_dumpf (in, 4607 stderr, 4608 JSON_INDENT (2)); 4609 global_ret = EXIT_FAILURE; 4610 GNUNET_SCHEDULER_shutdown (); 4611 json_decref (keys); 4612 return; 4613 } 4614 if (0 != 4615 GNUNET_memcmp (&master_pub, 4616 &mpub)) 4617 { 4618 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4619 "Fatal: exchange uses different master key!\n"); 4620 global_ret = EXIT_FAILURE; 4621 GNUNET_SCHEDULER_shutdown (); 4622 json_decref (keys); 4623 return; 4624 } 4625 if (GNUNET_SYSERR == 4626 tofu_check (&secmset)) 4627 { 4628 global_ret = EXIT_FAILURE; 4629 GNUNET_SCHEDULER_shutdown (); 4630 json_decref (keys); 4631 return; 4632 } 4633 if ( (GNUNET_OK != 4634 show_signkeys (&secmset.eddsa, 4635 signkeys)) || 4636 (GNUNET_OK != 4637 show_denomkeys (&secmset.rsa, 4638 &secmset.cs, 4639 denomkeys)) ) 4640 { 4641 global_ret = EXIT_FAILURE; 4642 GNUNET_SCHEDULER_shutdown (); 4643 json_decref (keys); 4644 return; 4645 } 4646 json_decref (keys); 4647 next (args); 4648 } 4649 4650 4651 /** 4652 * Sign @a signkeys with offline key. 4653 * 4654 * @param secm_pub security module public key used to sign the denominations 4655 * @param signkeys keys to output 4656 * @param[in,out] result array where to output the signatures 4657 * @return #GNUNET_OK on success 4658 */ 4659 static enum GNUNET_GenericReturnValue 4660 sign_signkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub, 4661 const json_t *signkeys, 4662 json_t *result) 4663 { 4664 size_t index; 4665 json_t *value; 4666 4667 json_array_foreach (signkeys, index, value) { 4668 const char *err_name; 4669 unsigned int err_line; 4670 struct TALER_ExchangePublicKeyP exchange_pub; 4671 struct TALER_SecurityModuleSignatureP secm_sig; 4672 struct GNUNET_TIME_Timestamp start_time; 4673 struct GNUNET_TIME_Timestamp sign_end; 4674 struct GNUNET_TIME_Timestamp legal_end; 4675 struct GNUNET_TIME_Relative duration; 4676 struct GNUNET_JSON_Specification spec[] = { 4677 GNUNET_JSON_spec_timestamp ("stamp_start", 4678 &start_time), 4679 GNUNET_JSON_spec_timestamp ("stamp_expire", 4680 &sign_end), 4681 GNUNET_JSON_spec_timestamp ("stamp_end", 4682 &legal_end), 4683 GNUNET_JSON_spec_fixed_auto ("key", 4684 &exchange_pub), 4685 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_sig", 4686 &secm_sig), 4687 GNUNET_JSON_spec_end () 4688 }; 4689 4690 if (GNUNET_OK != 4691 GNUNET_JSON_parse (value, 4692 spec, 4693 &err_name, 4694 &err_line)) 4695 { 4696 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4697 "Invalid input for signing key to 'show': %s #%u at %u (skipping)\n", 4698 err_name, 4699 err_line, 4700 (unsigned int) index); 4701 json_dumpf (value, 4702 stderr, 4703 JSON_INDENT (2)); 4704 global_ret = EXIT_FAILURE; 4705 GNUNET_SCHEDULER_shutdown (); 4706 return GNUNET_SYSERR; 4707 } 4708 4709 duration = GNUNET_TIME_absolute_get_difference (start_time.abs_time, 4710 sign_end.abs_time); 4711 if (GNUNET_OK != 4712 TALER_exchange_secmod_eddsa_verify (&exchange_pub, 4713 start_time, 4714 duration, 4715 secm_pub, 4716 &secm_sig)) 4717 { 4718 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4719 "Invalid security module signature for signing key %s (aborting)\n", 4720 TALER_B2S (&exchange_pub)); 4721 global_ret = EXIT_FAILURE; 4722 GNUNET_SCHEDULER_shutdown (); 4723 GNUNET_JSON_parse_free (spec); 4724 return GNUNET_SYSERR; 4725 } 4726 { 4727 struct TALER_MasterSignatureP master_sig; 4728 4729 TALER_exchange_offline_signkey_validity_sign (&exchange_pub, 4730 start_time, 4731 sign_end, 4732 legal_end, 4733 &master_priv, 4734 &master_sig); 4735 GNUNET_assert (0 == 4736 json_array_append_new ( 4737 result, 4738 GNUNET_JSON_PACK ( 4739 GNUNET_JSON_pack_data_auto ("exchange_pub", 4740 &exchange_pub), 4741 GNUNET_JSON_pack_data_auto ("master_sig", 4742 &master_sig)))); 4743 } 4744 GNUNET_JSON_parse_free (spec); 4745 } 4746 return GNUNET_OK; 4747 } 4748 4749 4750 /** 4751 * Looks up the AGE_RESTRICTED setting for a denomination in the config and 4752 * returns the age restriction (mask) accordingly. 4753 * 4754 * @param section_name Section in the configuration for the particular 4755 * denomination. 4756 */ 4757 static struct TALER_AgeMask 4758 load_age_mask (const char*section_name) 4759 { 4760 static const struct TALER_AgeMask null_mask = {0}; 4761 enum GNUNET_GenericReturnValue ret; 4762 4763 if (! ar_enabled) 4764 return null_mask; 4765 4766 if (GNUNET_OK != (GNUNET_CONFIGURATION_have_value ( 4767 kcfg, 4768 section_name, 4769 "AGE_RESTRICTED"))) 4770 return null_mask; 4771 4772 ret = GNUNET_CONFIGURATION_get_value_yesno (kcfg, 4773 section_name, 4774 "AGE_RESTRICTED"); 4775 if (GNUNET_SYSERR == ret) 4776 GNUNET_log_config_invalid (GNUNET_ERROR_TYPE_ERROR, 4777 section_name, 4778 "AGE_RESTRICTED", 4779 "Value must be YES or NO\n"); 4780 if (GNUNET_YES == ret) 4781 return ar_config.mask; 4782 4783 return null_mask; 4784 } 4785 4786 4787 /** 4788 * Sign @a denomkeys with offline key. 4789 * 4790 * @param secm_pub_rsa security module public key used to sign the RSA denominations 4791 * @param secm_pub_cs security module public key used to sign the CS denominations 4792 * @param denomkeys keys to output 4793 * @param[in,out] result array where to output the signatures 4794 * @return #GNUNET_OK on success 4795 */ 4796 static enum GNUNET_GenericReturnValue 4797 sign_denomkeys (const struct TALER_SecurityModulePublicKeyP *secm_pub_rsa, 4798 const struct TALER_SecurityModulePublicKeyP *secm_pub_cs, 4799 const json_t *denomkeys, 4800 json_t *result) 4801 { 4802 size_t index; 4803 json_t *value; 4804 4805 json_array_foreach (denomkeys, index, value) { 4806 const char *err_name; 4807 unsigned int err_line; 4808 const char *section_name; 4809 struct TALER_DenominationPublicKey denom_pub; 4810 struct GNUNET_TIME_Timestamp stamp_start; 4811 struct GNUNET_TIME_Timestamp stamp_expire_withdraw; 4812 struct GNUNET_TIME_Timestamp stamp_expire_deposit; 4813 struct GNUNET_TIME_Timestamp stamp_expire_legal; 4814 struct TALER_Amount coin_value; 4815 struct TALER_DenomFeeSet fees; 4816 struct TALER_SecurityModuleSignatureP secm_sig; 4817 struct GNUNET_JSON_Specification spec[] = { 4818 GNUNET_JSON_spec_string ("section_name", 4819 §ion_name), 4820 TALER_JSON_spec_denom_pub ("denom_pub", 4821 &denom_pub), 4822 TALER_JSON_spec_amount ("value", 4823 currency, 4824 &coin_value), 4825 TALER_JSON_SPEC_DENOM_FEES ("fee", 4826 currency, 4827 &fees), 4828 GNUNET_JSON_spec_timestamp ("stamp_start", 4829 &stamp_start), 4830 GNUNET_JSON_spec_timestamp ("stamp_expire_withdraw", 4831 &stamp_expire_withdraw), 4832 GNUNET_JSON_spec_timestamp ("stamp_expire_deposit", 4833 &stamp_expire_deposit), 4834 GNUNET_JSON_spec_timestamp ("stamp_expire_legal", 4835 &stamp_expire_legal), 4836 GNUNET_JSON_spec_fixed_auto ("denom_secmod_sig", 4837 &secm_sig), 4838 GNUNET_JSON_spec_end () 4839 }; 4840 struct GNUNET_TIME_Relative duration; 4841 struct TALER_DenominationHashP h_denom_pub; 4842 4843 if (GNUNET_OK != 4844 GNUNET_JSON_parse (value, 4845 spec, 4846 &err_name, 4847 &err_line)) 4848 { 4849 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4850 "Invalid input for denomination key to 'sign': %s #%u at %u (skipping)\n", 4851 err_name, 4852 err_line, 4853 (unsigned int) index); 4854 json_dumpf (value, 4855 stderr, 4856 JSON_INDENT (2)); 4857 GNUNET_JSON_parse_free (spec); 4858 global_ret = EXIT_FAILURE; 4859 GNUNET_SCHEDULER_shutdown (); 4860 return GNUNET_SYSERR; 4861 } 4862 duration = GNUNET_TIME_absolute_get_difference ( 4863 stamp_start.abs_time, 4864 stamp_expire_withdraw.abs_time); 4865 4866 /* Load the age mask, if applicable to this denomination */ 4867 denom_pub.age_mask = load_age_mask (section_name); 4868 4869 TALER_denom_pub_hash (&denom_pub, 4870 &h_denom_pub); 4871 4872 switch (denom_pub.bsign_pub_key->cipher) 4873 { 4874 case GNUNET_CRYPTO_BSA_RSA: 4875 { 4876 struct TALER_RsaPubHashP h_rsa; 4877 4878 TALER_rsa_pub_hash (denom_pub.bsign_pub_key->details.rsa_public_key, 4879 &h_rsa); 4880 if (GNUNET_OK != 4881 TALER_exchange_secmod_rsa_verify (&h_rsa, 4882 section_name, 4883 stamp_start, 4884 duration, 4885 secm_pub_rsa, 4886 &secm_sig)) 4887 { 4888 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4889 "Invalid security module signature for denomination key %s (aborting)\n", 4890 GNUNET_h2s (&h_denom_pub.hash)); 4891 global_ret = EXIT_FAILURE; 4892 GNUNET_SCHEDULER_shutdown (); 4893 GNUNET_JSON_parse_free (spec); 4894 return GNUNET_SYSERR; 4895 } 4896 } 4897 break; 4898 case GNUNET_CRYPTO_BSA_CS: 4899 { 4900 struct TALER_CsPubHashP h_cs; 4901 4902 TALER_cs_pub_hash (&denom_pub.bsign_pub_key->details.cs_public_key, 4903 &h_cs); 4904 if (GNUNET_OK != 4905 TALER_exchange_secmod_cs_verify (&h_cs, 4906 section_name, 4907 stamp_start, 4908 duration, 4909 secm_pub_cs, 4910 &secm_sig)) 4911 { 4912 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 4913 "Invalid security module signature for denomination key %s (aborting)\n", 4914 GNUNET_h2s (&h_denom_pub.hash)); 4915 global_ret = EXIT_FAILURE; 4916 GNUNET_SCHEDULER_shutdown (); 4917 GNUNET_JSON_parse_free (spec); 4918 return GNUNET_SYSERR; 4919 } 4920 } 4921 break; 4922 default: 4923 global_ret = EXIT_FAILURE; 4924 GNUNET_SCHEDULER_shutdown (); 4925 GNUNET_JSON_parse_free (spec); 4926 return GNUNET_SYSERR; 4927 } 4928 4929 { 4930 struct TALER_MasterSignatureP master_sig; 4931 4932 TALER_exchange_offline_denom_validity_sign (&h_denom_pub, 4933 stamp_start, 4934 stamp_expire_withdraw, 4935 stamp_expire_deposit, 4936 stamp_expire_legal, 4937 &coin_value, 4938 &fees, 4939 &master_priv, 4940 &master_sig); 4941 GNUNET_assert (0 == 4942 json_array_append_new ( 4943 result, 4944 GNUNET_JSON_PACK ( 4945 GNUNET_JSON_pack_data_auto ("h_denom_pub", 4946 &h_denom_pub), 4947 GNUNET_JSON_pack_data_auto ("master_sig", 4948 &master_sig)))); 4949 } 4950 GNUNET_JSON_parse_free (spec); 4951 } 4952 return GNUNET_OK; 4953 } 4954 4955 4956 /** 4957 * Sign future keys. 4958 * 4959 * @param args the array of command-line arguments to process next 4960 */ 4961 static void 4962 do_sign (char *const *args) 4963 { 4964 json_t *keys; 4965 const char *err_name; 4966 unsigned int err_line; 4967 const json_t *denomkeys; 4968 const json_t *signkeys; 4969 struct TALER_MasterPublicKeyP mpub; 4970 struct TALER_SecurityModulePublicKeySetP secmset; 4971 struct GNUNET_JSON_Specification spec[] = { 4972 GNUNET_JSON_spec_array_const ("future_denoms", 4973 &denomkeys), 4974 GNUNET_JSON_spec_array_const ("future_signkeys", 4975 &signkeys), 4976 GNUNET_JSON_spec_fixed_auto ("master_pub", 4977 &mpub), 4978 GNUNET_JSON_spec_fixed_auto ("denom_secmod_public_key", 4979 &secmset.rsa), 4980 GNUNET_JSON_spec_fixed_auto ("denom_secmod_cs_public_key", 4981 &secmset.cs), 4982 GNUNET_JSON_spec_fixed_auto ("signkey_secmod_public_key", 4983 &secmset.eddsa), 4984 GNUNET_JSON_spec_end () 4985 }; 4986 4987 keys = parse_keys_input ("sign"); 4988 if (NULL == keys) 4989 return; 4990 if (GNUNET_OK != 4991 load_offline_key (GNUNET_NO)) 4992 { 4993 json_decref (keys); 4994 return; 4995 } 4996 if (GNUNET_OK != 4997 GNUNET_JSON_parse (keys, 4998 spec, 4999 &err_name, 5000 &err_line)) 5001 { 5002 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5003 "Invalid input to 'sign' : %s #%u (skipping)\n", 5004 err_name, 5005 err_line); 5006 json_dumpf (in, 5007 stderr, 5008 JSON_INDENT (2)); 5009 global_ret = EXIT_FAILURE; 5010 GNUNET_SCHEDULER_shutdown (); 5011 json_decref (keys); 5012 return; 5013 } 5014 if (0 != 5015 GNUNET_memcmp (&master_pub, 5016 &mpub)) 5017 { 5018 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5019 "Fatal: exchange uses different master key!\n"); 5020 global_ret = EXIT_FAILURE; 5021 GNUNET_SCHEDULER_shutdown (); 5022 json_decref (keys); 5023 return; 5024 } 5025 if (GNUNET_SYSERR == 5026 tofu_check (&secmset)) 5027 { 5028 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5029 "Fatal: security module keys changed!\n"); 5030 global_ret = EXIT_FAILURE; 5031 GNUNET_SCHEDULER_shutdown (); 5032 json_decref (keys); 5033 return; 5034 } 5035 { 5036 json_t *signkey_sig_array = json_array (); 5037 json_t *denomkey_sig_array = json_array (); 5038 5039 GNUNET_assert (NULL != signkey_sig_array); 5040 GNUNET_assert (NULL != denomkey_sig_array); 5041 if ( (GNUNET_OK != 5042 sign_signkeys (&secmset.eddsa, 5043 signkeys, 5044 signkey_sig_array)) || 5045 (GNUNET_OK != 5046 sign_denomkeys (&secmset.rsa, 5047 &secmset.cs, 5048 denomkeys, 5049 denomkey_sig_array)) ) 5050 { 5051 global_ret = EXIT_FAILURE; 5052 GNUNET_SCHEDULER_shutdown (); 5053 json_decref (signkey_sig_array); 5054 json_decref (denomkey_sig_array); 5055 json_decref (keys); 5056 return; 5057 } 5058 5059 output_operation (OP_UPLOAD_SIGS, 5060 GNUNET_JSON_PACK ( 5061 GNUNET_JSON_pack_array_steal ("denom_sigs", 5062 denomkey_sig_array), 5063 GNUNET_JSON_pack_array_steal ("signkey_sigs", 5064 signkey_sig_array))); 5065 } 5066 json_decref (keys); 5067 next (args); 5068 } 5069 5070 5071 /** 5072 * Setup and output offline signing key. 5073 * 5074 * @param args the array of command-line arguments to process next 5075 */ 5076 static void 5077 do_setup (char *const *args) 5078 { 5079 if (GNUNET_OK != 5080 load_offline_key (GNUNET_YES)) 5081 { 5082 global_ret = EXIT_NOPERMISSION; 5083 return; 5084 } 5085 if (NULL != *args) 5086 { 5087 output_operation (OP_SETUP, 5088 GNUNET_JSON_PACK ( 5089 GNUNET_JSON_pack_data_auto ("exchange_offline_pub", 5090 &master_pub))); 5091 } 5092 5093 else 5094 { 5095 char *pub_s; 5096 5097 pub_s = GNUNET_STRINGS_data_to_string_alloc (&master_pub, 5098 sizeof (master_pub)); 5099 fprintf (stdout, 5100 "%s\n", 5101 pub_s); 5102 GNUNET_free (pub_s); 5103 } 5104 if ( (NULL != *args) && 5105 (0 == strcmp (*args, 5106 "-")) ) 5107 args++; 5108 next (args); 5109 } 5110 5111 5112 /** 5113 * Print the current extensions as configured 5114 * 5115 * @param args the array of command-line arguments to process next 5116 */ 5117 static void 5118 do_extensions_show (char *const *args) 5119 { 5120 const struct TALER_Extensions *it; 5121 json_t *exts = json_object (); 5122 json_t *obj; 5123 5124 GNUNET_assert (NULL != exts); 5125 for (it = TALER_extensions_get_head (); 5126 NULL != it && NULL != it->extension; 5127 it = it->next) 5128 { 5129 const struct TALER_Extension *extension = it->extension; 5130 int ret; 5131 5132 ret = json_object_set_new (exts, 5133 extension->name, 5134 extension->manifest (extension)); 5135 GNUNET_assert (-1 != ret); 5136 } 5137 5138 obj = GNUNET_JSON_PACK ( 5139 GNUNET_JSON_pack_object_steal ("extensions", 5140 exts)); 5141 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5142 "%s\n", 5143 json_dumps (obj, 5144 JSON_INDENT (2))); 5145 json_decref (obj); 5146 next (args); 5147 } 5148 5149 5150 /** 5151 * Sign the configurations of the enabled extensions 5152 */ 5153 static void 5154 do_extensions_sign (char *const *args) 5155 { 5156 json_t *extensions = json_object (); 5157 struct TALER_ExtensionManifestsHashP h_manifests; 5158 struct TALER_MasterSignatureP sig; 5159 const struct TALER_Extensions *it; 5160 bool found = false; 5161 json_t *obj; 5162 5163 GNUNET_assert (NULL != extensions); 5164 for (it = TALER_extensions_get_head (); 5165 NULL != it && NULL != it->extension; 5166 it = it->next) 5167 { 5168 const struct TALER_Extension *ext = it->extension; 5169 GNUNET_assert (ext); 5170 5171 found = true; 5172 5173 GNUNET_assert (0 == 5174 json_object_set_new (extensions, 5175 ext->name, 5176 ext->manifest (ext))); 5177 } 5178 5179 if (! found) 5180 return; 5181 5182 if (GNUNET_OK != 5183 TALER_JSON_extensions_manifests_hash (extensions, 5184 &h_manifests)) 5185 { 5186 json_decref (extensions); 5187 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5188 "error while hashing manifest for extensions\n"); 5189 return; 5190 } 5191 5192 if (GNUNET_OK != 5193 load_offline_key (GNUNET_NO)) 5194 { 5195 json_decref (extensions); 5196 return; 5197 } 5198 5199 TALER_exchange_offline_extension_manifests_hash_sign (&h_manifests, 5200 &master_priv, 5201 &sig); 5202 obj = GNUNET_JSON_PACK ( 5203 GNUNET_JSON_pack_object_steal ("extensions", 5204 extensions), 5205 GNUNET_JSON_pack_data_auto ( 5206 "extensions_sig", 5207 &sig)); 5208 5209 output_operation (OP_EXTENSIONS, 5210 obj); 5211 next (args); 5212 } 5213 5214 5215 /** 5216 * Dispatch @a args in the @a cmds array. 5217 * 5218 * @param args arguments with subcommand to dispatch 5219 * @param cmds array of possible subcommands to call 5220 */ 5221 static void 5222 cmd_handler (char *const *args, 5223 const struct SubCommand *cmds) 5224 { 5225 nxt = NULL; 5226 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5227 { 5228 if (0 == strcasecmp (cmds[i].name, 5229 args[0])) 5230 { 5231 cmds[i].cb (&args[1]); 5232 return; 5233 } 5234 } 5235 5236 if (0 != strcasecmp ("help", 5237 args[0])) 5238 { 5239 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5240 "Unexpected command `%s'\n", 5241 args[0]); 5242 global_ret = EXIT_INVALIDARGUMENT; 5243 } 5244 5245 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5246 "Supported subcommands:\n"); 5247 for (unsigned int i = 0; NULL != cmds[i].name; i++) 5248 { 5249 GNUNET_log (GNUNET_ERROR_TYPE_MESSAGE, 5250 "- %s: %s\n", 5251 cmds[i].name, 5252 cmds[i].help); 5253 } 5254 json_decref (out); 5255 out = NULL; 5256 GNUNET_SCHEDULER_shutdown (); 5257 } 5258 5259 5260 static void 5261 do_work_extensions (char *const *args) 5262 { 5263 struct SubCommand cmds[] = { 5264 { 5265 .name = "show", 5266 .help = 5267 "show the extensions in the Taler-config and their configured parameters", 5268 .cb = &do_extensions_show 5269 }, 5270 { 5271 .name = "sign", 5272 .help = 5273 "sign the configuration of the extensions and publish it with the exchange", 5274 .cb = &do_extensions_sign 5275 }, 5276 { 5277 .name = NULL, 5278 } 5279 }; 5280 5281 if (NULL == args[0]) 5282 { 5283 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 5284 "You must provide a subcommand: `show` or `sign`.\n"); 5285 GNUNET_SCHEDULER_shutdown (); 5286 global_ret = EXIT_INVALIDARGUMENT; 5287 return; 5288 } 5289 5290 cmd_handler (args, cmds); 5291 } 5292 5293 5294 static void 5295 work (void *cls) 5296 { 5297 char *const *args = cls; 5298 struct SubCommand cmds[] = { 5299 { 5300 .name = "setup", 5301 .help = 5302 "initialize offline key signing material and display public offline key", 5303 .cb = &do_setup 5304 }, 5305 { 5306 .name = "download", 5307 .help = 5308 "obtain future public keys from exchange (to be performed online!)", 5309 .cb = &do_download 5310 }, 5311 { 5312 .name = "show", 5313 .help = 5314 "display future public keys from exchange for human review (pass '-' as argument to disable consuming input)", 5315 .cb = &do_show 5316 }, 5317 { 5318 .name = "sign", 5319 .help = "sign all future public keys from the input", 5320 .cb = &do_sign 5321 }, 5322 { 5323 .name = "revoke-denomination", 5324 .help = 5325 "revoke denomination key (hash of public key must be given as argument)", 5326 .cb = &do_revoke_denomination_key 5327 }, 5328 { 5329 .name = "revoke-signkey", 5330 .help = 5331 "revoke exchange online signing key (public key must be given as argument)", 5332 .cb = &do_revoke_signkey 5333 }, 5334 { 5335 .name = "enable-auditor", 5336 .help = 5337 "enable auditor for the exchange (auditor-public key, auditor-URI and auditor-name must be given as arguments)", 5338 .cb = &do_add_auditor 5339 }, 5340 { 5341 .name = "disable-auditor", 5342 .help = 5343 "disable auditor at the exchange (auditor-public key must be given as argument)", 5344 .cb = &do_del_auditor 5345 }, 5346 { 5347 .name = "enable-account", 5348 .help = 5349 "enable wire account of the exchange (payto-URI must be given as argument; for optional arguments see man page)", 5350 .cb = &do_add_wire 5351 }, 5352 { 5353 .name = "disable-account", 5354 .help = 5355 "disable wire account of the exchange (payto-URI must be given as argument)", 5356 .cb = &do_del_wire 5357 }, 5358 { 5359 .name = "wire-fee", 5360 .help = 5361 "sign wire fees for the given year (year, wire method, wire fee, and closing fee must be given as arguments)", 5362 .cb = &do_set_wire_fee 5363 }, 5364 { 5365 .name = "global-fee", 5366 .help = 5367 "sign global fees for the given year (year, history fee, account fee, purse fee, purse timeout, history expiration and the maximum number of free purses per account must be given as arguments)", 5368 .cb = &do_set_global_fee 5369 }, 5370 { 5371 .name = "drain", 5372 .help = 5373 "drain profits from exchange escrow account to regular exchange operator account (amount, debit account configuration section and credit account payto://-URI must be given as arguments)", 5374 .cb = &do_drain 5375 }, 5376 { 5377 .name = "add-partner", 5378 .help = 5379 "add partner exchange for P2P wad transfers (partner master public key, partner base URL, wad fee, wad frequency and validity year must be given as arguments)", 5380 .cb = &do_add_partner 5381 }, 5382 { 5383 .name = "aml-enable", 5384 .help = 5385 "enable AML staff member (staff member public key, legal name and rw (read write) or ro (read only) must be given as arguments)", 5386 .cb = &enable_aml_staff 5387 }, 5388 { 5389 .name = "aml-disable", 5390 .help = 5391 "disable AML staff member (staff member public key and legal name must be given as arguments)", 5392 .cb = &disable_aml_staff 5393 }, 5394 { 5395 .name = "upload", 5396 .help = 5397 "upload operation result to exchange (to be performed online!)", 5398 .cb = &do_upload 5399 }, 5400 { 5401 .name = "extensions", 5402 .help = "subcommands for extension handling", 5403 .cb = &do_work_extensions 5404 }, 5405 /* list terminator */ 5406 { 5407 .name = NULL, 5408 } 5409 }; 5410 (void) cls; 5411 5412 cmd_handler (args, cmds); 5413 } 5414 5415 5416 /** 5417 * Main function that will be run. 5418 * 5419 * @param cls closure 5420 * @param args remaining command-line arguments 5421 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 5422 * @param cfg configuration 5423 */ 5424 static void 5425 run (void *cls, 5426 char *const *args, 5427 const char *cfgfile, 5428 const struct GNUNET_CONFIGURATION_Handle *cfg) 5429 { 5430 (void) cls; 5431 (void) cfgfile; 5432 kcfg = cfg; 5433 5434 /* load extensions */ 5435 GNUNET_assert (GNUNET_OK == 5436 TALER_extensions_init (kcfg)); 5437 5438 /* setup age restriction, if applicable */ 5439 { 5440 const struct TALER_AgeRestrictionConfig *arc; 5441 5442 if (NULL != 5443 (arc = TALER_extensions_get_age_restriction_config ())) 5444 { 5445 ar_config = *arc; 5446 ar_enabled = true; 5447 } 5448 } 5449 5450 5451 if (GNUNET_OK != 5452 TALER_config_get_currency (kcfg, 5453 "exchange", 5454 ¤cy)) 5455 { 5456 global_ret = EXIT_NOTCONFIGURED; 5457 return; 5458 } 5459 5460 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 5461 &rc); 5462 rc = GNUNET_CURL_gnunet_rc_create (ctx); 5463 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 5464 NULL); 5465 next (args); 5466 } 5467 5468 5469 /** 5470 * The main function of the taler-exchange-offline tool. This tool is used to 5471 * create the signing and denomination keys for the exchange. It uses the 5472 * long-term offline private key and generates signatures with it. It also 5473 * supports online operations with the exchange to download its input data and 5474 * to upload its results. Those online operations should be performed on 5475 * another machine in production! 5476 * 5477 * @param argc number of arguments from the command line 5478 * @param argv command line arguments 5479 * @return 0 ok, 1 on error 5480 */ 5481 int 5482 main (int argc, 5483 char *const *argv) 5484 { 5485 struct GNUNET_GETOPT_CommandLineOption options[] = { 5486 GNUNET_GETOPT_OPTION_END 5487 }; 5488 enum GNUNET_GenericReturnValue ret; 5489 5490 ret = GNUNET_PROGRAM_run ( 5491 TALER_EXCHANGE_project_data (), 5492 argc, argv, 5493 "taler-exchange-offline", 5494 gettext_noop ("Operations for offline signing for a Taler exchange"), 5495 options, 5496 &run, NULL); 5497 if (GNUNET_SYSERR == ret) 5498 return EXIT_INVALIDARGUMENT; 5499 if (GNUNET_NO == ret) 5500 return EXIT_SUCCESS; 5501 return global_ret; 5502 } 5503 5504 5505 /* end of taler-exchange-offline.c */