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