taler-exchange-wire-gateway-client.c (24421B)
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 "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 (&wtid, 461 sizeof (wtid)); 462 } 463 params = strchr (credit_account.full_payto, 464 (unsigned char) '&'); 465 if (NULL != params) 466 *params = '\0'; 467 TALER_BANK_prepare_transfer (credit_account, 468 &amount, 469 "http://exchange.example.com/", 470 &wtid, 471 metadata, 472 &buf, 473 &buf_size); 474 eh = TALER_BANK_transfer (ctx, 475 &auth, 476 buf, 477 buf_size, 478 &confirmation_cb, 479 NULL); 480 GNUNET_free (buf); 481 if (NULL == eh) 482 { 483 fprintf (stderr, 484 "Could not execute the wire transfer\n"); 485 GNUNET_SCHEDULER_shutdown (); 486 return; 487 } 488 } 489 490 491 /** 492 * Function called with the result of the operation. 493 * 494 * @param cls closure 495 * @param air response details 496 */ 497 static void 498 res_cb (void *cls, 499 const struct TALER_BANK_AdminAddIncomingResponse *air) 500 { 501 (void) cls; 502 op = NULL; 503 switch (air->http_status) 504 { 505 case MHD_HTTP_OK: 506 global_ret = 0; 507 fprintf (stdout, 508 "%llu\n", 509 (unsigned long long) air->details.ok.serial_id); 510 break; 511 default: 512 fprintf (stderr, 513 "Operation failed with status code %u/%u\n", 514 (unsigned int) air->ec, 515 air->http_status); 516 if (NULL != air->response) 517 json_dumpf (air->response, 518 stderr, 519 JSON_INDENT (2)); 520 break; 521 } 522 GNUNET_SCHEDULER_shutdown (); 523 } 524 525 526 /** 527 * Ask the bank to execute a wire transfer to the exchange. 528 */ 529 static void 530 execute_admin_transfer (void) 531 { 532 struct TALER_ReservePublicKeyP reserve_pub; 533 534 if (NULL != subject) 535 { 536 if (GNUNET_OK != 537 GNUNET_STRINGS_string_to_data (subject, 538 strlen (subject), 539 &reserve_pub, 540 sizeof (reserve_pub))) 541 { 542 fprintf (stderr, 543 "Error: wire transfer subject must be a reserve public key\n"); 544 return; 545 } 546 } 547 else 548 { 549 /* pick one that is kind-of well-formed at random */ 550 GNUNET_CRYPTO_random_block (&reserve_pub, 551 sizeof (reserve_pub)); 552 } 553 op = TALER_BANK_admin_add_incoming (ctx, 554 &auth, 555 &reserve_pub, 556 &amount, 557 debit_account, 558 &res_cb, 559 NULL); 560 if (NULL == op) 561 { 562 fprintf (stderr, 563 "Could not execute the wire transfer to the exchange\n"); 564 GNUNET_SCHEDULER_shutdown (); 565 return; 566 } 567 } 568 569 570 /** 571 * Run the actual main operation requested by the user. 572 */ 573 static void 574 execute_tasks (void) 575 { 576 if (GNUNET_YES == incoming_history) 577 { 578 execute_credit_history (); 579 return; 580 } 581 if (GNUNET_YES == outgoing_history) 582 { 583 execute_debit_history (); 584 return; 585 } 586 if (NULL != credit_account.full_payto) 587 { 588 execute_wire_transfer (); 589 return; 590 } 591 if (NULL != debit_account.full_payto) 592 { 593 execute_admin_transfer (); 594 return; 595 } 596 597 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 598 "No operation specified.\n"); 599 global_ret = 0; 600 GNUNET_SCHEDULER_shutdown (); 601 } 602 603 604 /** 605 * Receives an access token to the bank. 606 * 607 * @param cls closure 608 * @param atr response details 609 */ 610 static void 611 access_token_cb ( 612 void *cls, 613 const struct TALER_BANK_AccountTokenResponse *atr) 614 { 615 (void) cls; 616 ath = NULL; 617 switch (atr->ec) 618 { 619 case TALER_EC_NONE: 620 break; /* continued below */ 621 default: 622 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 623 "Failed to get access token: %s (%u/%d)\n", 624 TALER_ErrorCode_get_hint (atr->ec), 625 atr->http_status, 626 (int) atr->ec); 627 global_ret = EXIT_NOPERMISSION; 628 GNUNET_SCHEDULER_shutdown (); 629 return; 630 } 631 GNUNET_assert (TALER_BANK_AUTH_BASIC == auth.method); 632 GNUNET_free (auth.details.basic.username); 633 GNUNET_free (auth.details.basic.password); 634 auth.method = TALER_BANK_AUTH_BEARER; 635 auth.details.bearer.token = GNUNET_strdup (atr->details.ok.access_token); 636 execute_tasks (); 637 } 638 639 640 /** 641 * Main function that will be run. 642 * 643 * @param cls closure 644 * @param args remaining command-line arguments 645 * @param cfgfile name of the configuration file used (for saving, can be NULL!) 646 * @param cfg configuration 647 */ 648 static void 649 run (void *cls, 650 char *const *args, 651 const char *cfgfile, 652 const struct GNUNET_CONFIGURATION_Handle *cfg) 653 { 654 enum TALER_BANK_TokenScope scope; 655 (void) cls; 656 (void) args; 657 (void) cfgfile; 658 (void) cfg; 659 660 GNUNET_SCHEDULER_add_shutdown (&do_shutdown, 661 NULL); 662 ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule, 663 &rc); 664 GNUNET_assert (NULL != ctx); 665 rc = GNUNET_CURL_gnunet_rc_create (ctx); 666 if (NULL != account_section) 667 { 668 if (0 != strncasecmp ("exchange-accountcredentials-", 669 account_section, 670 strlen ("exchange-accountcredentials-"))) 671 { 672 fprintf (stderr, 673 "Error: invalid section specified, must begin with `%s`\n", 674 "exchange-accountcredentials-"); 675 GNUNET_SCHEDULER_shutdown (); 676 return; 677 } 678 if ( (NULL != auth.wire_gateway_url) || 679 (NULL != auth.details.basic.username) || 680 (NULL != auth.details.basic.password) ) 681 { 682 fprintf (stderr, 683 "Error: Conflicting authentication options provided. Please only use one method.\n"); 684 GNUNET_SCHEDULER_shutdown (); 685 return; 686 } 687 if (GNUNET_OK != 688 TALER_BANK_auth_parse_cfg (cfg, 689 account_section, 690 &auth)) 691 { 692 fprintf (stderr, 693 "Error: Authentication information not found in configuration section `%s'\n", 694 account_section); 695 GNUNET_SCHEDULER_shutdown (); 696 return; 697 } 698 } 699 else 700 { 701 if ( (NULL != auth.wire_gateway_url) && 702 (NULL != auth.details.basic.username) && 703 (NULL != auth.details.basic.password) ) 704 { 705 auth.method = TALER_BANK_AUTH_BASIC; 706 } 707 else if ( (NULL != auth.wire_gateway_url) && 708 (NULL != auth.details.bearer.token) ) 709 { 710 auth.method = TALER_BANK_AUTH_BEARER; 711 } 712 713 else if (NULL == auth.wire_gateway_url) 714 { 715 fprintf (stderr, 716 "Error: No account specified (use -b or -s options).\n"); 717 GNUNET_SCHEDULER_shutdown (); 718 return; 719 } 720 } 721 if ( (NULL == auth.wire_gateway_url) || 722 (0 == strlen (auth.wire_gateway_url)) || 723 (0 != strncasecmp ("http", 724 auth.wire_gateway_url, 725 strlen ("http"))) ) 726 { 727 fprintf (stderr, 728 "Error: Invalid wire gateway URL `%s' configured.\n", 729 auth.wire_gateway_url); 730 GNUNET_SCHEDULER_shutdown (); 731 return; 732 } 733 if ( (GNUNET_YES == incoming_history) && 734 (GNUNET_YES == outgoing_history) ) 735 { 736 fprintf (stderr, 737 "Error: Please specify only -i or -o, but not both.\n"); 738 GNUNET_SCHEDULER_shutdown (); 739 return; 740 } 741 if ( (NULL != auth.core_bank_url) && 742 (TALER_BANK_AUTH_BASIC == auth.method) ) 743 { 744 scope = TALER_BANK_TOKEN_SCOPE_READONLY; 745 if (NULL != credit_account.full_payto) 746 scope = TALER_BANK_TOKEN_SCOPE_WIREGATEWAY; 747 if (NULL != debit_account.full_payto) 748 scope = TALER_BANK_TOKEN_SCOPE_READWRITE; 749 ath = TALER_BANK_account_token (ctx, 750 &auth, 751 auth.details.basic.username, // FIXME: why? correct? 752 scope, 753 false, /* refreshable */ 754 "taler-exchange-wire-gateway-client CLI token", 755 GNUNET_TIME_UNIT_MINUTES, 756 &access_token_cb, 757 NULL); 758 if (NULL == ath) 759 { 760 GNUNET_break (0); 761 GNUNET_SCHEDULER_shutdown (); 762 return; 763 } 764 } 765 else 766 { 767 if (TALER_BANK_AUTH_BASIC == auth.method) 768 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 769 "No CORE_BANK_URL given in `%s' and using basic authentication. Not all taler-wire-gateway implementations allow this.\n", 770 account_section); 771 execute_tasks (); 772 } 773 } 774 775 776 /** 777 * The main function of the taler-exchange-wire-gateway-client 778 * 779 * @param argc number of arguments from the command line 780 * @param argv command line arguments 781 * @return 0 ok, 1 on error 782 */ 783 int 784 main (int argc, 785 char *const *argv) 786 { 787 const struct GNUNET_GETOPT_CommandLineOption options[] = { 788 TALER_getopt_get_amount ('a', 789 "amount", 790 "VALUE", 791 "value to transfer", 792 &amount), 793 GNUNET_GETOPT_option_string ('b', 794 "bank", 795 "URL", 796 "Wire gateway URL to use to talk to the bank", 797 &auth.wire_gateway_url), 798 GNUNET_GETOPT_option_string ('C', 799 "credit", 800 "ACCOUNT", 801 "payto URI of the bank account to credit (when making outgoing transfers)", 802 &credit_account.full_payto), 803 GNUNET_GETOPT_option_string ('D', 804 "debit", 805 "PAYTO-URL", 806 "payto URI of the bank account to debit (when making incoming transfers)", 807 &debit_account.full_payto), 808 GNUNET_GETOPT_option_flag ('i', 809 "credit-history", 810 "Ask to get a list of 10 incoming transactions.", 811 &incoming_history), 812 GNUNET_GETOPT_option_flag ('o', 813 "debit-history", 814 "Ask to get a list of 10 outgoing transactions.", 815 &outgoing_history), 816 GNUNET_GETOPT_option_string ('p', 817 "pass", 818 "PASSPHRASE", 819 "passphrase to use for authentication", 820 &auth.details.basic.password), 821 GNUNET_GETOPT_option_string ('s', 822 "section", 823 "ACCOUNT-SECTION", 824 "Which config section has the credentials to access the bank. Conflicts with -b -u and -p options.\n", 825 &account_section), 826 GNUNET_GETOPT_option_string ('S', 827 "subject", 828 "SUBJECT", 829 "specifies the wire transfer subject", 830 &subject), 831 GNUNET_GETOPT_option_string ('u', 832 "user", 833 "USERNAME", 834 "username to use for authentication", 835 &auth.details.basic.username), 836 GNUNET_GETOPT_option_ulong ('w', 837 "since-when", 838 "ROW", 839 "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.", 840 &start_row), 841 GNUNET_GETOPT_option_string ('x', 842 "extra", 843 "METADATA", 844 "additional metadata text to pass to the core banking system for an outgoing wire transfer", 845 &metadata), 846 GNUNET_GETOPT_OPTION_END 847 }; 848 enum GNUNET_GenericReturnValue ret; 849 850 global_ret = 1; 851 ret = GNUNET_PROGRAM_run ( 852 TALER_EXCHANGE_project_data (), 853 argc, argv, 854 "taler-wire-gateway-client", 855 gettext_noop ("Client tool of the Taler Wire Gateway"), 856 options, 857 &run, NULL); 858 if (GNUNET_SYSERR == ret) 859 return 3; 860 if (GNUNET_NO == ret) 861 return 0; 862 return global_ret; 863 } 864 865 866 /* end taler-wire-gateway-client.c */