exchange

Base system with REST service to issue digital coins, run by the payment service provider
Log | Files | Refs | Submodules | README | LICENSE

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 */