taler-exchange-wire-gateway-client.c (24549B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2017-2023, 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-wire-gateway-client.c 18 * @brief Execute wire transfer. 19 * @author Christian Grothoff 20 */ 21 #include "taler/platform.h" 22 #include <gnunet/gnunet_util_lib.h> 23 #include <gnunet/gnunet_json_lib.h> 24 #include <jansson.h> 25 #include <microhttpd.h> 26 #include "taler/taler_bank_service.h" 27 28 /** 29 * If set to #GNUNET_YES, then we'll ask the bank for a list 30 * of incoming transactions from the account. 31 */ 32 static int incoming_history; 33 34 /** 35 * If set to #GNUNET_YES, then we'll ask the bank for a list 36 * of outgoing transactions from the account. 37 */ 38 static int outgoing_history; 39 40 /** 41 * Amount to transfer. 42 */ 43 static struct TALER_Amount amount; 44 45 /** 46 * Credit account payto://-URI. 47 */ 48 static struct TALER_FullPayto credit_account; 49 50 /** 51 * Debit account payto://-URI. 52 */ 53 static struct TALER_FullPayto debit_account; 54 55 /** 56 * Wire transfer subject. 57 */ 58 static char *subject; 59 60 /** 61 * Which config section has the credentials to access the bank. 62 */ 63 static char *account_section; 64 65 /** 66 * -x command-line option. 67 */ 68 static char *metadata; 69 70 /** 71 * Starting row. 72 */ 73 static unsigned long long start_row = UINT64_MAX; 74 75 /** 76 * Authentication data. 77 */ 78 static struct TALER_BANK_AuthenticationData auth; 79 80 /** 81 * Return value from main(). 82 */ 83 static int global_ret = 1; 84 85 /** 86 * Main execution context for the main loop. 87 */ 88 static struct GNUNET_CURL_Context *ctx; 89 90 /** 91 * Handle to ongoing credit history operation. 92 */ 93 static struct TALER_BANK_CreditHistoryHandle *chh; 94 95 /** 96 * Handle to ongoing debit history operation. 97 */ 98 static struct TALER_BANK_DebitHistoryHandle *dhh; 99 100 /** 101 * Handle to fetch an access token. 102 */ 103 static struct TALER_BANK_AccountTokenHandle *ath; 104 105 /** 106 * Handle for executing the wire transfer. 107 */ 108 static struct TALER_BANK_TransferHandle *eh; 109 110 /** 111 * Handle to access the exchange. 112 */ 113 static struct TALER_BANK_AdminAddIncomingHandle *op; 114 115 /** 116 * Context for running the CURL event loop. 117 */ 118 static struct GNUNET_CURL_RescheduleContext *rc; 119 120 121 /** 122 * Function run when the test terminates (good or bad). 123 * Cleans up our state. 124 * 125 * @param cls NULL 126 */ 127 static void 128 do_shutdown (void *cls) 129 { 130 (void) cls; 131 if (NULL != op) 132 { 133 TALER_BANK_admin_add_incoming_cancel (op); 134 op = NULL; 135 } 136 if (NULL != chh) 137 { 138 TALER_BANK_credit_history_cancel (chh); 139 chh = NULL; 140 } 141 if (NULL != dhh) 142 { 143 TALER_BANK_debit_history_cancel (dhh); 144 dhh = NULL; 145 } 146 if (NULL != ath) 147 { 148 TALER_BANK_account_token_cancel (ath); 149 ath = NULL; 150 } 151 if (NULL != eh) 152 { 153 TALER_BANK_transfer_cancel (eh); 154 eh = NULL; 155 } 156 if (NULL != ctx) 157 { 158 GNUNET_CURL_fini (ctx); 159 ctx = NULL; 160 } 161 if (NULL != rc) 162 { 163 GNUNET_CURL_gnunet_rc_destroy (rc); 164 rc = NULL; 165 } 166 TALER_BANK_auth_free (&auth); 167 } 168 169 170 /** 171 * Callback used to process the transaction 172 * history returned by the bank. 173 * 174 * @param cls closure 175 * @param reply response we got from the bank 176 */ 177 static void 178 credit_history_cb (void *cls, 179 const struct TALER_BANK_CreditHistoryResponse *reply) 180 { 181 (void) cls; 182 183 chh = NULL; 184 switch (reply->http_status) 185 { 186 case 0: 187 fprintf (stderr, 188 "Failed to obtain HTTP reply from `%s'\n", 189 auth.wire_gateway_url); 190 global_ret = 2; 191 break; 192 case MHD_HTTP_NO_CONTENT: 193 fprintf (stdout, 194 "No transactions.\n"); 195 global_ret = 0; 196 break; 197 case MHD_HTTP_OK: 198 for (unsigned int i = 0; i<reply->details.ok.details_length; i++) 199 { 200 const struct TALER_BANK_CreditDetails *cd = 201 &reply->details.ok.details[i]; 202 203 /* If credit/debit accounts were specified, use as a filter */ 204 if ( (NULL != credit_account.full_payto) && 205 (0 != TALER_full_payto_cmp (credit_account, 206 reply->details.ok.credit_account_uri) ) ) 207 continue; 208 if ( (NULL != debit_account.full_payto) && 209 (0 != TALER_full_payto_cmp (debit_account, 210 cd->debit_account_uri) ) ) 211 continue; 212 switch (cd->type) 213 { 214 case TALER_BANK_CT_RESERVE: 215 fprintf (stdout, 216 "%llu: %s->%s (%s) over %s at %s\n", 217 (unsigned long long) cd->serial_id, 218 cd->debit_account_uri.full_payto, 219 reply->details.ok.credit_account_uri.full_payto, 220 TALER_B2S (&cd->details.reserve.reserve_pub), 221 TALER_amount2s (&cd->amount), 222 GNUNET_TIME_timestamp2s (cd->execution_date)); 223 break; 224 case TALER_BANK_CT_KYCAUTH: 225 fprintf (stdout, 226 "%llu: %s->%s (KYC:%s) over %s at %s\n", 227 (unsigned long long) cd->serial_id, 228 cd->debit_account_uri.full_payto, 229 reply->details.ok.credit_account_uri.full_payto, 230 TALER_B2S (&cd->details.kycauth.account_pub), 231 TALER_amount2s (&cd->amount), 232 GNUNET_TIME_timestamp2s (cd->execution_date)); 233 break; 234 case TALER_BANK_CT_WAD: 235 GNUNET_break (0); // FIXME-#7271 (support wad payments) 236 break; 237 } 238 } 239 global_ret = 0; 240 break; 241 default: 242 fprintf (stderr, 243 "Failed to obtain credit history from `%s': HTTP status %u (%s)\n", 244 auth.wire_gateway_url, 245 reply->http_status, 246 TALER_ErrorCode_get_hint (reply->ec)); 247 if (NULL != reply->response) 248 json_dumpf (reply->response, 249 stderr, 250 JSON_INDENT (2)); 251 global_ret = 2; 252 break; 253 } 254 GNUNET_SCHEDULER_shutdown (); 255 } 256 257 258 /** 259 * Ask the bank the list of transactions for the bank account 260 * mentioned in the config section given by the user. 261 */ 262 static void 263 execute_credit_history (void) 264 { 265 if (NULL != subject) 266 { 267 fprintf (stderr, 268 "Specifying subject is not supported when inspecting credit history\n"); 269 GNUNET_SCHEDULER_shutdown (); 270 return; 271 } 272 chh = TALER_BANK_credit_history (ctx, 273 &auth, 274 start_row, 275 -10, 276 GNUNET_TIME_UNIT_ZERO, 277 &credit_history_cb, 278 NULL); 279 if (NULL == chh) 280 { 281 fprintf (stderr, 282 "Could not request the credit transaction history.\n"); 283 GNUNET_SCHEDULER_shutdown (); 284 return; 285 } 286 } 287 288 289 /** 290 * Function with the debit transaction history. 291 * 292 * @param cls closure 293 * @param reply response details 294 */ 295 static void 296 debit_history_cb (void *cls, 297 const struct TALER_BANK_DebitHistoryResponse *reply) 298 { 299 (void) cls; 300 301 dhh = NULL; 302 switch (reply->http_status) 303 { 304 case 0: 305 fprintf (stderr, 306 "Failed to obtain HTTP reply from `%s'\n", 307 auth.wire_gateway_url); 308 global_ret = 2; 309 break; 310 case MHD_HTTP_NO_CONTENT: 311 fprintf (stdout, 312 "No transactions.\n"); 313 global_ret = 0; 314 break; 315 case MHD_HTTP_OK: 316 for (unsigned int i = 0; i<reply->details.ok.details_length; i++) 317 { 318 const struct TALER_BANK_DebitDetails *dd = 319 &reply->details.ok.details[i]; 320 321 /* If credit/debit accounts were specified, use as a filter */ 322 if ( (NULL != credit_account.full_payto) && 323 (0 != TALER_full_payto_cmp (credit_account, 324 dd->credit_account_uri) ) ) 325 continue; 326 if ( (NULL != debit_account.full_payto) && 327 (0 != TALER_full_payto_cmp (debit_account, 328 reply->details.ok.debit_account_uri) ) ) 329 continue; 330 fprintf (stdout, 331 "%llu: %s->%s (%s) over %s at %s\n", 332 (unsigned long long) dd->serial_id, 333 reply->details.ok.debit_account_uri.full_payto, 334 dd->credit_account_uri.full_payto, 335 TALER_B2S (&dd->wtid), 336 TALER_amount2s (&dd->amount), 337 GNUNET_TIME_timestamp2s (dd->execution_date)); 338 } 339 global_ret = 0; 340 break; 341 default: 342 fprintf (stderr, 343 "Failed to obtain debit history from `%s': HTTP status %u (%s)\n", 344 auth.wire_gateway_url, 345 reply->http_status, 346 TALER_ErrorCode_get_hint (reply->ec)); 347 if (NULL != reply->response) 348 json_dumpf (reply->response, 349 stderr, 350 JSON_INDENT (2)); 351 global_ret = 2; 352 break; 353 } 354 GNUNET_SCHEDULER_shutdown (); 355 } 356 357 358 /** 359 * Ask the bank the list of transactions for the bank account 360 * mentioned in the config section given by the user. 361 */ 362 static void 363 execute_debit_history (void) 364 { 365 if (NULL != subject) 366 { 367 fprintf (stderr, 368 "Specifying subject is not supported when inspecting debit history\n"); 369 GNUNET_SCHEDULER_shutdown (); 370 return; 371 } 372 dhh = TALER_BANK_debit_history (ctx, 373 &auth, 374 start_row, 375 -10, 376 GNUNET_TIME_UNIT_ZERO, 377 &debit_history_cb, 378 NULL); 379 if (NULL == dhh) 380 { 381 fprintf (stderr, 382 "Could not request the debit transaction history.\n"); 383 GNUNET_SCHEDULER_shutdown (); 384 return; 385 } 386 } 387 388 389 /** 390 * Callback that processes the outcome of a wire transfer 391 * execution. 392 * 393 * @param cls closure 394 * @param tr response details 395 */ 396 static void 397 confirmation_cb (void *cls, 398 const struct TALER_BANK_TransferResponse *tr) 399 { 400 (void) cls; 401 eh = NULL; 402 if (MHD_HTTP_OK != tr->http_status) 403 { 404 fprintf (stderr, 405 "The wire transfer didn't execute correctly (%u/%d).\n", 406 tr->http_status, 407 tr->ec); 408 GNUNET_SCHEDULER_shutdown (); 409 return; 410 } 411 412 fprintf (stdout, 413 "Wire transfer #%llu executed successfully at %s.\n", 414 (unsigned long long) tr->details.ok.row_id, 415 GNUNET_TIME_timestamp2s (tr->details.ok.timestamp)); 416 global_ret = 0; 417 GNUNET_SCHEDULER_shutdown (); 418 } 419 420 421 /** 422 * Ask the bank to execute a wire transfer. 423 */ 424 static void 425 execute_wire_transfer (void) 426 { 427 struct TALER_WireTransferIdentifierRawP wtid; 428 void *buf; 429 size_t buf_size; 430 char *params; 431 432 if (NULL != debit_account.full_payto) 433 { 434 fprintf (stderr, 435 "Invalid option -C specified, conflicts with -D\n"); 436 GNUNET_SCHEDULER_shutdown (); 437 return; 438 } 439 440 /* See if subject was given as a payto-parameter. */ 441 if (NULL == subject) 442 subject = TALER_payto_get_subject (credit_account); 443 if (NULL != subject) 444 { 445 if (GNUNET_OK != 446 GNUNET_STRINGS_string_to_data (subject, 447 strlen (subject), 448 &wtid, 449 sizeof (wtid))) 450 { 451 fprintf (stderr, 452 "Error: wire transfer subject must be a WTID\n"); 453 GNUNET_SCHEDULER_shutdown (); 454 return; 455 } 456 } 457 else 458 { 459 /* pick one at random */ 460 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 461 &wtid, 462 sizeof (wtid)); 463 } 464 params = strchr (credit_account.full_payto, 465 (unsigned char) '&'); 466 if (NULL != params) 467 *params = '\0'; 468 TALER_BANK_prepare_transfer (credit_account, 469 &amount, 470 "http://exchange.example.com/", 471 &wtid, 472 metadata, 473 &buf, 474 &buf_size); 475 eh = TALER_BANK_transfer (ctx, 476 &auth, 477 buf, 478 buf_size, 479 &confirmation_cb, 480 NULL); 481 GNUNET_free (buf); 482 if (NULL == eh) 483 { 484 fprintf (stderr, 485 "Could not execute the wire transfer\n"); 486 GNUNET_SCHEDULER_shutdown (); 487 return; 488 } 489 } 490 491 492 /** 493 * Function called with the result of the operation. 494 * 495 * @param cls closure 496 * @param air response details 497 */ 498 static void 499 res_cb (void *cls, 500 const struct TALER_BANK_AdminAddIncomingResponse *air) 501 { 502 (void) cls; 503 op = NULL; 504 switch (air->http_status) 505 { 506 case MHD_HTTP_OK: 507 global_ret = 0; 508 fprintf (stdout, 509 "%llu\n", 510 (unsigned long long) air->details.ok.serial_id); 511 break; 512 default: 513 fprintf (stderr, 514 "Operation failed with status code %u/%u\n", 515 (unsigned int) air->ec, 516 air->http_status); 517 if (NULL != air->response) 518 json_dumpf (air->response, 519 stderr, 520 JSON_INDENT (2)); 521 break; 522 } 523 GNUNET_SCHEDULER_shutdown (); 524 } 525 526 527 /** 528 * Ask the bank to execute a wire transfer to the exchange. 529 */ 530 static void 531 execute_admin_transfer (void) 532 { 533 struct TALER_ReservePublicKeyP reserve_pub; 534 535 if (NULL != subject) 536 { 537 if (GNUNET_OK != 538 GNUNET_STRINGS_string_to_data (subject, 539 strlen (subject), 540 &reserve_pub, 541 sizeof (reserve_pub))) 542 { 543 fprintf (stderr, 544 "Error: wire transfer subject must be a reserve public key\n"); 545 return; 546 } 547 } 548 else 549 { 550 /* pick one that is kind-of well-formed at random */ 551 GNUNET_CRYPTO_random_block (GNUNET_CRYPTO_QUALITY_NONCE, 552 &reserve_pub, 553 sizeof (reserve_pub)); 554 } 555 op = TALER_BANK_admin_add_incoming (ctx, 556 &auth, 557 &reserve_pub, 558 &amount, 559 debit_account, 560 &res_cb, 561 NULL); 562 if (NULL == op) 563 { 564 fprintf (stderr, 565 "Could not execute the wire transfer to the exchange\n"); 566 GNUNET_SCHEDULER_shutdown (); 567 return; 568 } 569 } 570 571 572 /** 573 * Run the actual main operation requested by the user. 574 */ 575 static void 576 execute_tasks (void) 577 { 578 if (GNUNET_YES == incoming_history) 579 { 580 execute_credit_history (); 581 return; 582 } 583 if (GNUNET_YES == outgoing_history) 584 { 585 execute_debit_history (); 586 return; 587 } 588 if (NULL != credit_account.full_payto) 589 { 590 execute_wire_transfer (); 591 return; 592 } 593 if (NULL != debit_account.full_payto) 594 { 595 execute_admin_transfer (); 596 return; 597 } 598 599 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 600 "No operation specified.\n"); 601 global_ret = 0; 602 GNUNET_SCHEDULER_shutdown (); 603 } 604 605 606 /** 607 * Receives an access token to the bank. 608 * 609 * @param cls closure 610 * @param atr response details 611 */ 612 static void 613 access_token_cb ( 614 void *cls, 615 const struct TALER_BANK_AccountTokenResponse *atr) 616 { 617 (void) cls; 618 ath = NULL; 619 switch (atr->ec) 620 { 621 case TALER_EC_NONE: 622 break; /* continued below */ 623 default: 624 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 625 "Failed to get access token: %s (%u/%d)\n", 626 TALER_ErrorCode_get_hint (atr->ec), 627 atr->http_status, 628 (int) atr->ec); 629 global_ret = EXIT_NOPERMISSION; 630 GNUNET_SCHEDULER_shutdown (); 631 return; 632 } 633 GNUNET_assert (TALER_BANK_AUTH_BASIC == auth.method); 634 GNUNET_free (auth.details.basic.username); 635 GNUNET_free (auth.details.basic.password); 636 auth.method = TALER_BANK_AUTH_BEARER; 637 auth.details.bearer.token = GNUNET_strdup (atr->details.ok.access_token); 638 execute_tasks (); 639 } 640 641 642 /** 643 * Main function that will be run. 644 * 645 * @param cls closure 646 * @param args remaining command-line arguments 647 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 648 * @param cfg configuration 649 */ 650 static void 651 run (void *cls, 652 char *const *args, 653 const char *cfgfile, 654 const struct GNUNET_CONFIGURATION_Handle *cfg) 655 { 656 enum TALER_BANK_TokenScope scope; 657 (void) cls; 658 (void) args; 659 (void) cfgfile; 660 (void) cfg; 661 662 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 663 NULL); 664 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 665 &rc); 666 GNUNET_assert (NULL != ctx); 667 rc = GNUNET_CURL_gnunet_rc_create (ctx); 668 if (NULL != account_section) 669 { 670 if (0 != strncasecmp ("exchange-accountcredentials-", 671 account_section, 672 strlen ("exchange-accountcredentials-"))) 673 { 674 fprintf (stderr, 675 "Error: invalid section specified, must begin with `%s`\n", 676 "exchange-accountcredentials-"); 677 GNUNET_SCHEDULER_shutdown (); 678 return; 679 } 680 if ( (NULL != auth.wire_gateway_url) || 681 (NULL != auth.details.basic.username) || 682 (NULL != auth.details.basic.password) ) 683 { 684 fprintf (stderr, 685 "Error: Conflicting authentication options provided. Please only use one method.\n"); 686 GNUNET_SCHEDULER_shutdown (); 687 return; 688 } 689 if (GNUNET_OK != 690 TALER_BANK_auth_parse_cfg (cfg, 691 account_section, 692 &auth)) 693 { 694 fprintf (stderr, 695 "Error: Authentication information not found in configuration section `%s'\n", 696 account_section); 697 GNUNET_SCHEDULER_shutdown (); 698 return; 699 } 700 } 701 else 702 { 703 if ( (NULL != auth.wire_gateway_url) && 704 (NULL != auth.details.basic.username) && 705 (NULL != auth.details.basic.password) ) 706 { 707 auth.method = TALER_BANK_AUTH_BASIC; 708 } 709 else if ( (NULL != auth.wire_gateway_url) && 710 (NULL != auth.details.bearer.token) ) 711 { 712 auth.method = TALER_BANK_AUTH_BEARER; 713 } 714 715 else if (NULL == auth.wire_gateway_url) 716 { 717 fprintf (stderr, 718 "Error: No account specified (use -b or -s options).\n"); 719 GNUNET_SCHEDULER_shutdown (); 720 return; 721 } 722 } 723 if ( (NULL == auth.wire_gateway_url) || 724 (0 == strlen (auth.wire_gateway_url)) || 725 (0 != strncasecmp ("http", 726 auth.wire_gateway_url, 727 strlen ("http"))) ) 728 { 729 fprintf (stderr, 730 "Error: Invalid wire gateway URL `%s' configured.\n", 731 auth.wire_gateway_url); 732 GNUNET_SCHEDULER_shutdown (); 733 return; 734 } 735 if ( (GNUNET_YES == incoming_history) && 736 (GNUNET_YES == outgoing_history) ) 737 { 738 fprintf (stderr, 739 "Error: Please specify only -i or -o, but not both.\n"); 740 GNUNET_SCHEDULER_shutdown (); 741 return; 742 } 743 if ( (NULL != auth.core_bank_url) && 744 (TALER_BANK_AUTH_BASIC == auth.method) ) 745 { 746 scope = TALER_BANK_TOKEN_SCOPE_READONLY; 747 if (NULL != credit_account.full_payto) 748 scope = TALER_BANK_TOKEN_SCOPE_WIREGATEWAY; 749 if (NULL != debit_account.full_payto) 750 scope = TALER_BANK_TOKEN_SCOPE_READWRITE; 751 ath = TALER_BANK_account_token (ctx, 752 &auth, 753 auth.details.basic.username, // FIXME: why? correct? 754 scope, 755 false, /* refreshable */ 756 "taler-exchange-wire-gateway-client CLI token", 757 GNUNET_TIME_UNIT_MINUTES, 758 &access_token_cb, 759 NULL); 760 if (NULL == ath) 761 { 762 GNUNET_break (0); 763 GNUNET_SCHEDULER_shutdown (); 764 return; 765 } 766 } 767 else 768 { 769 if (TALER_BANK_AUTH_BASIC == auth.method) 770 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 771 "No CORE_BANK_URL given in `%s' and using basic authentication. Not all taler-wire-gateway implementations allow this.\n", 772 account_section); 773 execute_tasks (); 774 } 775 } 776 777 778 /** 779 * The main function of the taler-exchange-wire-gateway-client 780 * 781 * @param argc number of arguments from the command line 782 * @param argv command line arguments 783 * @return 0 ok, 1 on error 784 */ 785 int 786 main (int argc, 787 char *const *argv) 788 { 789 const struct GNUNET_GETOPT_CommandLineOption options[] = { 790 TALER_getopt_get_amount ('a', 791 "amount", 792 "VALUE", 793 "value to transfer", 794 &amount), 795 GNUNET_GETOPT_option_string ('b', 796 "bank", 797 "URL", 798 "Wire gateway URL to use to talk to the bank", 799 &auth.wire_gateway_url), 800 GNUNET_GETOPT_option_string ('C', 801 "credit", 802 "ACCOUNT", 803 "payto URI of the bank account to credit (when making outgoing transfers)", 804 &credit_account.full_payto), 805 GNUNET_GETOPT_option_string ('D', 806 "debit", 807 "PAYTO-URL", 808 "payto URI of the bank account to debit (when making incoming transfers)", 809 &debit_account.full_payto), 810 GNUNET_GETOPT_option_flag ('i', 811 "credit-history", 812 "Ask to get a list of 10 incoming transactions.", 813 &incoming_history), 814 GNUNET_GETOPT_option_flag ('o', 815 "debit-history", 816 "Ask to get a list of 10 outgoing transactions.", 817 &outgoing_history), 818 GNUNET_GETOPT_option_string ('p', 819 "pass", 820 "PASSPHRASE", 821 "passphrase to use for authentication", 822 &auth.details.basic.password), 823 GNUNET_GETOPT_option_string ('s', 824 "section", 825 "ACCOUNT-SECTION", 826 "Which config section has the credentials to access the bank. Conflicts with -b -u and -p options.\n", 827 &account_section), 828 GNUNET_GETOPT_option_string ('S', 829 "subject", 830 "SUBJECT", 831 "specifies the wire transfer subject", 832 &subject), 833 GNUNET_GETOPT_option_string ('u', 834 "user", 835 "USERNAME", 836 "username to use for authentication", 837 &auth.details.basic.username), 838 GNUNET_GETOPT_option_ulong ('w', 839 "since-when", 840 "ROW", 841 "When asking the bank for transactions history, this option commands that all the results should have IDs settled after SW. If not given, then the 10 youngest transactions are returned.", 842 &start_row), 843 GNUNET_GETOPT_option_string ('x', 844 "extra", 845 "METADATA", 846 "additional metadata text to pass to the core banking system for an outgoing wire transfer", 847 &metadata), 848 GNUNET_GETOPT_OPTION_END 849 }; 850 enum GNUNET_GenericReturnValue ret; 851 852 global_ret = 1; 853 ret = GNUNET_PROGRAM_run ( 854 TALER_EXCHANGE_project_data (), 855 argc, argv, 856 "taler-wire-gateway-client", 857 gettext_noop ("Client tool of the Taler Wire Gateway"), 858 options, 859 &run, NULL); 860 if (GNUNET_SYSERR == ret) 861 return 3; 862 if (GNUNET_NO == ret) 863 return 0; 864 return global_ret; 865 } 866 867 868 /* end taler-wire-gateway-client.c */