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