LCOV - code coverage report
Current view: top level - wire - plugin_wire_test.c (source / functions) Hit Total Coverage
Test: rcoverage.info Lines: 231 334 69.2 %
Date: 2017-09-17 17:24:28 Functions: 14 16 87.5 %

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2016 GNUnet e.V. & Inria
       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             : /**
      18             :  * @file plugin_wire_test.c
      19             :  * @brief plugin for the "test" wire method
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "platform.h"
      23             : #include "taler_wire_plugin.h"
      24             : #include "taler_bank_service.h"
      25             : #include "taler_signatures.h"
      26             : #include <gnunet/gnunet_curl_lib.h>
      27             : 
      28             : /* only for HTTP status codes */
      29             : #include <microhttpd.h>
      30             : 
      31             : /**
      32             :  * Type of the "cls" argument given to each of the functions in
      33             :  * our API.
      34             :  */
      35             : struct TestClosure
      36             : {
      37             : 
      38             :   /**
      39             :    * Which currency do we support?
      40             :    */
      41             :   char *currency;
      42             : 
      43             :   /**
      44             :    * URI of our bank.
      45             :    */
      46             :   char *bank_uri;
      47             : 
      48             :   /**
      49             :    * Authentication information.
      50             :    */
      51             :   struct TALER_BANK_AuthenticationData auth;
      52             : 
      53             :   /**
      54             :    * Handle to the context for sending funds to the bank.
      55             :    */
      56             :   struct GNUNET_CURL_Context *ctx;
      57             : 
      58             :   /**
      59             :    * Scheduler context for running the @e ctx.
      60             :    */
      61             :   struct GNUNET_CURL_RescheduleContext *rc;
      62             : 
      63             :   /**
      64             :    * Number of the account that the exchange has at the bank for
      65             :    * transfers.
      66             :    */
      67             :   unsigned long long exchange_account_no;
      68             : 
      69             : };
      70             : 
      71             : 
      72             : /**
      73             :  * Handle returned by #test_prepare_wire_transfer.
      74             :  */
      75             : struct TALER_WIRE_PrepareHandle
      76             : {
      77             : 
      78             :   /**
      79             :    * Task we use for async execution.
      80             :    */
      81             :   struct GNUNET_SCHEDULER_Task *task;
      82             : 
      83             :   /**
      84             :    * Test closure we run in.
      85             :    */
      86             :   struct TestClosure *tc;
      87             : 
      88             :   /**
      89             :    * Wire data for the transfer.
      90             :    */
      91             :   json_t *wire;
      92             : 
      93             :   /**
      94             :    * Base URL to use for the exchange.
      95             :    */
      96             :   char *exchange_base_url;
      97             : 
      98             :   /**
      99             :    * Function to call with the serialized data.
     100             :    */
     101             :   TALER_WIRE_PrepareTransactionCallback ptc;
     102             : 
     103             :   /**
     104             :    * Closure for @e ptc.
     105             :    */
     106             :   void *ptc_cls;
     107             : 
     108             :   /**
     109             :    * Amount to transfer.
     110             :    */
     111             :   struct TALER_Amount amount;
     112             : 
     113             :   /**
     114             :    * Subject of the wire transfer.
     115             :    */
     116             :   struct TALER_WireTransferIdentifierRawP wtid;
     117             : 
     118             : 
     119             : };
     120             : 
     121             : 
     122             : /**
     123             :  * Handle returned by #test_execute_wire_transfer.
     124             :  */
     125             : struct TALER_WIRE_ExecuteHandle
     126             : {
     127             : 
     128             :   /**
     129             :    * Handle to the HTTP request to the bank.
     130             :    */
     131             :   struct TALER_BANK_AdminAddIncomingHandle *aaih;
     132             : 
     133             :   /**
     134             :    * Function to call with the result.
     135             :    */
     136             :   TALER_WIRE_ConfirmationCallback cc;
     137             : 
     138             :   /**
     139             :    * Closure for @e cc.
     140             :    */
     141             :   void *cc_cls;
     142             : };
     143             : 
     144             : 
     145             : 
     146             : /**
     147             :  * Round amount DOWN to the amount that can be transferred via the wire
     148             :  * method.  For example, Taler may support 0.000001 EUR as a unit of
     149             :  * payment, but SEPA only supports 0.01 EUR.  This function would
     150             :  * round 0.125 EUR to 0.12 EUR in this case.
     151             :  *
     152             :  * @param cls the @e cls of this struct with the plugin-specific state
     153             :  * @param[in,out] amount amount to round down
     154             :  * @return #GNUNET_OK on success, #GNUNET_NO if rounding was unnecessary,
     155             :  *         #GNUNET_SYSERR if the amount or currency was invalid
     156             :  */
     157             : static int
     158          21 : test_amount_round (void *cls,
     159             :                    struct TALER_Amount *amount)
     160             : {
     161          21 :   struct TestClosure *tc = cls;
     162             :   uint32_t delta;
     163             : 
     164          21 :   if (NULL == tc->currency)
     165             :   {
     166           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     167             :                                "taler",
     168             :                                "CURRENCY");
     169           0 :     return GNUNET_SYSERR; /* not configured with currency */
     170             :   }
     171          21 :   if (0 != strcasecmp (amount->currency,
     172          21 :                        tc->currency))
     173             :   {
     174           1 :     GNUNET_break (0);
     175           1 :     return GNUNET_SYSERR;
     176             :   }
     177             :   /* 'test' method supports 1/100 of the unit currency, i.e. 0.01 CUR */
     178          20 :   delta = amount->fraction % (TALER_AMOUNT_FRAC_BASE / 100);
     179          20 :   if (0 == delta)
     180          16 :     return GNUNET_NO;
     181           4 :   amount->fraction -= delta;
     182           4 :   return GNUNET_OK;
     183             : }
     184             : 
     185             : 
     186             : /**
     187             :  * Compute purpose for signing.
     188             :  *
     189             :  * @param account number of the account
     190             :  * @param bank_uri URI of the bank
     191             :  * @param[out] wsd purpose to be signed
     192             :  */
     193             : static void
     194           7 : compute_purpose (uint64_t account,
     195             :                  const char *bank_uri,
     196             :                  struct TALER_MasterWireDetailsPS *wsd)
     197             : {
     198             :   struct GNUNET_HashContext *hc;
     199           7 :   uint64_t n = GNUNET_htonll (account);
     200             : 
     201           7 :   wsd->purpose.size = htonl (sizeof (struct TALER_MasterWireDetailsPS));
     202           7 :   wsd->purpose.purpose = htonl (TALER_SIGNATURE_MASTER_TEST_DETAILS);
     203           7 :   hc = GNUNET_CRYPTO_hash_context_start ();
     204           7 :   GNUNET_CRYPTO_hash_context_read (hc,
     205             :                                    "test",
     206             :                                    strlen ("test") + 1);
     207           7 :   GNUNET_CRYPTO_hash_context_read (hc,
     208             :                                    &n,
     209             :                                    sizeof (n));
     210           7 :   GNUNET_CRYPTO_hash_context_read (hc,
     211             :                                    bank_uri,
     212           7 :                                    strlen (bank_uri) + 1);
     213           7 :   GNUNET_CRYPTO_hash_context_finish (hc,
     214             :                                      &wsd->h_sepa_details);
     215           7 : }
     216             : 
     217             : 
     218             : /**
     219             :  * Check if the given wire format JSON object is correctly formatted.
     220             :  * Right now, the only thing we require is a field
     221             :  * "account_number" which must contain a positive 53-bit integer.
     222             :  *
     223             :  * @param cls the @e cls of this struct with the plugin-specific state
     224             :  * @param wire the JSON wire format object
     225             :  * @param master_pub public key of the exchange to verify against
     226             :  * @param[out] emsg set to an error message, unless we return #TALER_EC_NONE;
     227             :  *             error message must be freed by the caller using GNUNET_free()
     228             :  * @return #TALER_EC_NONE if correctly formatted
     229             :  */
     230             : static enum TALER_ErrorCode
     231          63 : test_wire_validate (void *cls,
     232             :                     const json_t *wire,
     233             :                     const struct TALER_MasterPublicKeyP *master_pub,
     234             :                     char **emsg)
     235             : {
     236          63 :   struct TestClosure *tc = cls;
     237             :   json_error_t error;
     238             :   json_int_t account_no;
     239             :   const char *bank_uri;
     240             :   const char *sig_s;
     241             :   struct TALER_MasterWireDetailsPS wsd;
     242             :   struct TALER_MasterSignatureP sig;
     243             : 
     244          63 :   *emsg = NULL;
     245          63 :   if (0 !=
     246          63 :       json_unpack_ex ((json_t *) wire,
     247             :                       &error,
     248             :                       0,
     249             :                       "{s:I, s:s}",
     250             :                       "account_number", &account_no,
     251             :                       "bank_uri", &bank_uri))
     252             :   {
     253             :     char *dump;
     254             : 
     255           0 :     dump = json_dumps (wire, 0);
     256           0 :     GNUNET_asprintf (emsg,
     257             :                      "JSON parsing failed at %s:%u: %s (%s): %s\n",
     258             :                      __FILE__, __LINE__,
     259             :                      error.text,
     260             :                      error.source,
     261             :                      dump);
     262           0 :     free (dump);
     263           0 :     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_JSON;
     264             :   }
     265         126 :   if ( (account_no < 0) ||
     266          63 :        (account_no > (1LL << 53)) )
     267             :   {
     268           0 :     GNUNET_asprintf (emsg,
     269             :                      "Account number %llu outside of permitted range\n",
     270             :                      account_no);
     271           0 :     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_ACCOUNT_NUMBER;
     272             :   }
     273         124 :   if ( (NULL != tc->bank_uri) &&
     274          61 :        (0 != strcmp (bank_uri,
     275          61 :                      tc->bank_uri)) )
     276             :   {
     277           0 :     GNUNET_asprintf (emsg,
     278             :                      "Wire specifies bank URI `%s', but this exchange only supports `%s'\n",
     279             :                      bank_uri,
     280             :                      tc->bank_uri);
     281           0 :     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_BANK;
     282             :   }
     283          63 :   if (NULL == master_pub)
     284             :   {
     285          57 :     GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     286             :                 "Skipping signature check as master public key not given\n");
     287          57 :     return TALER_EC_NONE;
     288             :   }
     289           6 :   if (0 !=
     290           6 :       json_unpack_ex ((json_t *) wire,
     291             :                       &error,
     292             :                       0,
     293             :                       "{s:s}",
     294             :                       "sig", &sig_s))
     295             :   {
     296           0 :     GNUNET_asprintf (emsg,
     297             :                      "Signature check required, but signature is missing\n");
     298           0 :     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE;
     299             :   }
     300           6 :   compute_purpose (account_no,
     301             :                    bank_uri,
     302             :                    &wsd);
     303           6 :   if (GNUNET_OK !=
     304           6 :       GNUNET_STRINGS_string_to_data (sig_s,
     305             :                                      strlen (sig_s),
     306             :                                      &sig,
     307             :                                      sizeof (sig)))
     308             :   {
     309           0 :     GNUNET_break (0);
     310           0 :     return GNUNET_SYSERR;
     311             :   }
     312           6 :   if (GNUNET_OK !=
     313           6 :       GNUNET_CRYPTO_eddsa_verify (TALER_SIGNATURE_MASTER_TEST_DETAILS,
     314             :                                   &wsd.purpose,
     315             :                                   &sig.eddsa_signature,
     316             :                                   &master_pub->eddsa_pub))
     317             :   {
     318           0 :     GNUNET_asprintf (emsg,
     319             :                      "Signature using public key `%s' invalid\n",
     320             :                      TALER_B2S (master_pub));
     321           0 :     return TALER_EC_DEPOSIT_INVALID_WIRE_FORMAT_SIGNATURE;
     322             :   }
     323           6 :   return TALER_EC_NONE;
     324             : }
     325             : 
     326             : 
     327             : /**
     328             :  * Obtain wire transfer details in the plugin-specific format
     329             :  * from the configuration.
     330             :  *
     331             :  * @param cls closure
     332             :  * @param cfg configuration with details about wire accounts
     333             :  * @param account_name which section in the configuration should we parse
     334             :  * @return NULL if @a cfg fails to have valid wire details for @a account_name
     335             :  */
     336             : static json_t *
     337           3 : test_get_wire_details (void *cls,
     338             :                        const struct GNUNET_CONFIGURATION_Handle *cfg,
     339             :                        const char *account_name)
     340             : {
     341           3 :   struct TestClosure *tc = cls;
     342             :   char *test_wire_file;
     343             :   json_error_t err;
     344             :   json_t *ret;
     345             :   char *emsg;
     346             : 
     347             :   /* Fetch reply */
     348           3 :   if (GNUNET_OK !=
     349           3 :       GNUNET_CONFIGURATION_get_value_filename (cfg,
     350             :                                                account_name,
     351             :                                                "TEST_RESPONSE_FILE",
     352             :                                                &test_wire_file))
     353             :   {
     354           0 :     GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
     355             :                                account_name,
     356             :                                "TEST_RESPONSE_FILE");
     357           0 :     return NULL;
     358             :   }
     359           3 :   ret = json_load_file (test_wire_file,
     360             :                         JSON_REJECT_DUPLICATES,
     361             :                         &err);
     362           3 :   if (NULL == ret)
     363             :   {
     364           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     365             :                 "Failed to parse JSON in %s: %s (%s:%u)\n",
     366             :                 test_wire_file,
     367             :                 err.text,
     368             :                 err.source,
     369             :                 err.line);
     370           0 :     GNUNET_free (test_wire_file);
     371           0 :     return NULL;
     372             :   }
     373           3 :   if (TALER_EC_NONE !=
     374           3 :       test_wire_validate (tc,
     375             :                           ret,
     376             :                           NULL,
     377             :                           &emsg))
     378             :   {
     379           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     380             :                 "Failed to validate TEST wire data in %s: %s\n",
     381             :                 test_wire_file,
     382             :                 emsg);
     383           0 :     GNUNET_free (emsg);
     384           0 :     GNUNET_free (test_wire_file);
     385           0 :     json_decref (ret);
     386           0 :     return NULL;
     387             :   }
     388           3 :   GNUNET_free (test_wire_file);
     389           3 :   return ret;
     390             : }
     391             : 
     392             : 
     393             : GNUNET_NETWORK_STRUCT_BEGIN
     394             : /**
     395             :  * Format we used for serialized transaction data.
     396             :  */
     397             : struct BufFormatP
     398             : {
     399             : 
     400             :   /**
     401             :    * The wire transfer identifier.
     402             :    */
     403             :   struct TALER_WireTransferIdentifierRawP wtid;
     404             : 
     405             :   /**
     406             :    * The amount.
     407             :    */
     408             :   struct TALER_AmountNBO amount;
     409             : 
     410             :   /* followed by serialized 'wire' JSON data (0-terminated) */
     411             : 
     412             :   /* followed by 0-terminated base URL */
     413             : 
     414             : };
     415             : GNUNET_NETWORK_STRUCT_END
     416             : 
     417             : 
     418             : /**
     419             :  * Abort preparation of a wire transfer. For example,
     420             :  * because we are shutting down.
     421             :  *
     422             :  * @param cls the @e cls of this struct with the plugin-specific state
     423             :  * @param pth preparation to cancel
     424             :  */
     425             : static void
     426          18 : test_prepare_wire_transfer_cancel (void *cls,
     427             :                                    struct TALER_WIRE_PrepareHandle *pth)
     428             : {
     429          18 :   if (NULL != pth->task)
     430           0 :     GNUNET_SCHEDULER_cancel (pth->task);
     431          18 :   json_decref (pth->wire);
     432          18 :   GNUNET_free (pth->exchange_base_url);
     433          18 :   GNUNET_free (pth);
     434          18 : }
     435             : 
     436             : 
     437             : /**
     438             :  * Prepare for exeuction of a wire transfer.  Calls the
     439             :  * callback with the serialized state.
     440             :  *
     441             :  * @param cls the `struct TALER_WIRE_PrepareHandle`
     442             :  */
     443             : static void
     444          18 : do_prepare (void *cls)
     445             : {
     446          18 :   struct TALER_WIRE_PrepareHandle *pth = cls;
     447             :   char *wire_enc;
     448             :   size_t len_w;
     449             :   size_t len_b;
     450             :   struct BufFormatP bf;
     451             : 
     452          18 :   pth->task = NULL;
     453             :   /* serialize the state into a 'buf' */
     454          18 :   wire_enc = json_dumps (pth->wire,
     455             :                          JSON_COMPACT | JSON_SORT_KEYS);
     456          18 :   if (NULL == wire_enc)
     457             :   {
     458           0 :     GNUNET_break (0);
     459           0 :     pth->ptc (pth->ptc_cls,
     460             :               NULL,
     461             :               0);
     462           0 :     test_prepare_wire_transfer_cancel (NULL,
     463             :                                        pth);
     464           0 :     return;
     465             :   }
     466          18 :   len_w = strlen (wire_enc) + 1;
     467          18 :   len_b = strlen (pth->exchange_base_url) + 1;
     468          18 :   bf.wtid = pth->wtid;
     469          18 :   TALER_amount_hton (&bf.amount,
     470          18 :                      &pth->amount);
     471          18 :   {
     472          18 :     char buf[sizeof (struct BufFormatP) + len_w + len_b];
     473             : 
     474          18 :     memcpy (buf,
     475             :             &bf,
     476             :             sizeof (struct BufFormatP));
     477          18 :     memcpy (&buf[sizeof (struct BufFormatP)],
     478             :             wire_enc,
     479             :             len_w);
     480          18 :     memcpy (&buf[sizeof (struct BufFormatP) + len_w],
     481          18 :             pth->exchange_base_url,
     482             :             len_b);
     483             : 
     484             :     /* finally give the state back */
     485          18 :     pth->ptc (pth->ptc_cls,
     486             :               buf,
     487             :               sizeof (buf));
     488             :   }
     489          18 :   free (wire_enc); /* not using GNUNET_free(),
     490             :                       as this one is allocated by libjansson */
     491          18 :   test_prepare_wire_transfer_cancel (NULL,
     492             :                                      pth);
     493             : }
     494             : 
     495             : 
     496             : /**
     497             :  * Prepare for exeuction of a wire transfer.  Note that we should call
     498             :  * @a ptc asynchronously (as that is what the API requires, because
     499             :  * some transfer methods need it).  So while we could immediately call
     500             :  * @a ptc, we first bundle up all the data and schedule a task to do
     501             :  * the work.
     502             :  *
     503             :  * @param cls the @e cls of this struct with the plugin-specific state
     504             :  * @param wire valid wire account information
     505             :  * @param amount amount to transfer, already rounded
     506             :  * @param exchange_base_url base URL of this exchange
     507             :  * @param wtid wire transfer identifier to use
     508             :  * @param ptc function to call with the prepared data to persist
     509             :  * @param ptc_cls closure for @a ptc
     510             :  * @return NULL on failure
     511             :  */
     512             : static struct TALER_WIRE_PrepareHandle *
     513          18 : test_prepare_wire_transfer (void *cls,
     514             :                             const json_t *wire,
     515             :                             const struct TALER_Amount *amount,
     516             :                             const char *exchange_base_url,
     517             :                             const struct TALER_WireTransferIdentifierRawP *wtid,
     518             :                             TALER_WIRE_PrepareTransactionCallback ptc,
     519             :                             void *ptc_cls)
     520             : {
     521          18 :   struct TestClosure *tc = cls;
     522             :   struct TALER_WIRE_PrepareHandle *pth;
     523             :   char *emsg;
     524             : 
     525          18 :   if (TALER_EC_NONE !=
     526          18 :       test_wire_validate (tc,
     527             :                           wire,
     528             :                           NULL,
     529             :                           &emsg))
     530             :   {
     531           0 :     GNUNET_break_op (0);
     532           0 :     GNUNET_free (emsg);
     533           0 :     return NULL;
     534             :   }
     535          18 :   pth = GNUNET_new (struct TALER_WIRE_PrepareHandle);
     536          18 :   pth->tc = tc;
     537          18 :   pth->wire = json_incref ((json_t *) wire);
     538          18 :   pth->exchange_base_url = GNUNET_strdup (exchange_base_url);
     539          18 :   pth->wtid = *wtid;
     540          18 :   pth->ptc = ptc;
     541          18 :   pth->ptc_cls = ptc_cls;
     542          18 :   pth->amount = *amount;
     543          18 :   pth->task = GNUNET_SCHEDULER_add_now (&do_prepare,
     544             :                                         pth);
     545          18 :   return pth;
     546             : }
     547             : 
     548             : 
     549             : /**
     550             :  * Called with the result of submitting information about an incoming
     551             :  * transaction to a bank.
     552             :  *
     553             :  * @param cls closure with the `struct TALER_WIRE_ExecuteHandle`
     554             :  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
     555             :  *                    0 if the bank's reply is bogus (fails to follow the protocol)
     556             :  * @param serial_id unique ID of the wire transfer in the bank's records; UINT64_MAX on error
     557             :  * @param json detailed response from the HTTPD, or NULL if reply was not JSON
     558             :  */
     559             : static void
     560          18 : execute_cb (void *cls,
     561             :             unsigned int http_status,
     562             :             uint64_t serial_id,
     563             :             const json_t *json)
     564             : {
     565          18 :   struct TALER_WIRE_ExecuteHandle *eh = cls;
     566             :   json_t *reason;
     567             :   const char *emsg;
     568             :   char *s;
     569             : 
     570          18 :   eh->aaih = NULL;
     571          18 :   emsg = NULL;
     572          18 :   if (NULL != json)
     573             :   {
     574          18 :     reason = json_object_get (json,
     575             :                               "reason");
     576          18 :     if (NULL != reason)
     577           0 :       emsg = json_string_value (reason);
     578             :   }
     579          18 :   if (NULL != emsg)
     580           0 :     GNUNET_asprintf (&s,
     581             :                      "%u (%s)",
     582             :                      http_status,
     583             :                      emsg);
     584             :   else
     585          18 :     GNUNET_asprintf (&s,
     586             :                      "%u",
     587             :                      http_status);
     588          18 :   eh->cc (eh->cc_cls,
     589             :           (MHD_HTTP_OK == http_status) ? GNUNET_OK : GNUNET_SYSERR,
     590             :           serial_id,
     591             :           (MHD_HTTP_OK == http_status) ? NULL : s);
     592          18 :   GNUNET_free (s);
     593          18 :   GNUNET_free (eh);
     594          18 : }
     595             : 
     596             : 
     597             : /**
     598             :  * Sign wire transfer details in the plugin-specific format.
     599             :  *
     600             :  * @param cls closure
     601             :  * @param in wire transfer details in JSON format
     602             :  * @param key private signing key to use
     603             :  * @param salt salt to add
     604             :  * @param[out] sig where to write the signature
     605             :  * @return #GNUNET_OK on success
     606             :  */
     607             : static int
     608           1 : test_sign_wire_details (void *cls,
     609             :                         const json_t *in,
     610             :                         const struct TALER_MasterPrivateKeyP *key,
     611             :                         const struct GNUNET_HashCode *salt,
     612             :                         struct TALER_MasterSignatureP *sig)
     613             : {
     614             :   struct TALER_MasterWireDetailsPS wsd;
     615             :   const char *bank_uri;
     616             :   const char *type;
     617             :   json_int_t account;
     618             :   json_error_t err;
     619             : 
     620           1 :   if (0 !=
     621           1 :       json_unpack_ex ((json_t *) in,
     622             :                       &err,
     623             :                       0 /* flags */,
     624             :                       "{s:s, s:s, s:I}",
     625             :                       "type", &type,
     626             :                       "bank_uri", &bank_uri,
     627             :                       "account_number", &account))
     628             :   {
     629           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     630             :                 "Failed to unpack JSON: %s (at %u)\n",
     631             :                 err.text,
     632             :                 err.position);
     633           0 :     return GNUNET_SYSERR;
     634             :   }
     635           1 :   if (0 != strcmp (type,
     636             :                    "test"))
     637             :   {
     638           0 :     GNUNET_log (GNUNET_ERROR_TYPE_WARNING,
     639             :                 "`type' must be `test' for test wire details\n");
     640           0 :     return GNUNET_SYSERR;
     641             :   }
     642           1 :   compute_purpose (account,
     643             :                    bank_uri,
     644             :                    &wsd);
     645           1 :   GNUNET_CRYPTO_eddsa_sign (&key->eddsa_priv,
     646             :                             &wsd.purpose,
     647             :                             &sig->eddsa_signature);
     648           1 :   return GNUNET_OK;
     649             : }
     650             : 
     651             : 
     652             : /**
     653             :  * Execute a wire transfer.
     654             :  *
     655             :  * @param cls the @e cls of this struct with the plugin-specific state
     656             :  * @param buf buffer with the prepared execution details
     657             :  * @param buf_size number of bytes in @a buf
     658             :  * @param cc function to call upon success
     659             :  * @param cc_cls closure for @a cc
     660             :  * @return NULL on error
     661             :  */
     662             : static struct TALER_WIRE_ExecuteHandle *
     663          18 : test_execute_wire_transfer (void *cls,
     664             :                             const char *buf,
     665             :                             size_t buf_size,
     666             :                             TALER_WIRE_ConfirmationCallback cc,
     667             :                             void *cc_cls)
     668             : {
     669          18 :   struct TestClosure *tc = cls;
     670             :   struct TALER_WIRE_ExecuteHandle *eh;
     671             :   json_t *wire;
     672             :   json_error_t error;
     673             :   struct TALER_Amount amount;
     674             :   json_int_t account_no;
     675             :   struct BufFormatP bf;
     676             :   char *emsg;
     677             :   const char *json_s;
     678             :   const char *exchange_base_url;
     679             : 
     680          18 :   if (NULL == tc->ctx)
     681             :   {
     682           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     683             :                 "Bank not initialized, cannot do transfers!\n");
     684           0 :     return NULL; /* not initialized with configuration, cannot do transfers */
     685             :   }
     686          36 :   if ( (buf_size <= sizeof (struct BufFormatP)) ||
     687          18 :        ('\0' != buf[buf_size - 1]) )
     688             :   {
     689           0 :     GNUNET_break (0);
     690           0 :     return NULL;
     691             :   }
     692          18 :   json_s = &buf[sizeof (struct BufFormatP)];
     693          18 :   exchange_base_url = &json_s[strlen (json_s) + 1];
     694          18 :   if (exchange_base_url > &buf[buf_size - 1])
     695             :   {
     696           0 :     GNUNET_break (0);
     697           0 :     return NULL;
     698             :   }
     699          18 :   memcpy (&bf,
     700             :           buf,
     701             :           sizeof (bf));
     702          18 :   TALER_amount_ntoh (&amount,
     703             :                      &bf.amount);
     704          18 :   wire = json_loads (json_s,
     705             :                      JSON_REJECT_DUPLICATES,
     706             :                      NULL);
     707          18 :   if (NULL == wire)
     708             :   {
     709           0 :     GNUNET_break (0);
     710           0 :     return NULL;
     711             :   }
     712          18 :   GNUNET_assert (TALER_EC_NONE ==
     713             :                  test_wire_validate (tc,
     714             :                                      wire,
     715             :                                      NULL,
     716             :                                      &emsg));
     717          18 :   if (0 !=
     718          18 :       json_unpack_ex (wire,
     719             :                       &error,
     720             :                       0,
     721             :                       "{s:I}",
     722             :                       "account_number", &account_no))
     723             :   {
     724           0 :     GNUNET_break (0);
     725           0 :     return NULL;
     726             :   }
     727             : 
     728          18 :   eh = GNUNET_new (struct TALER_WIRE_ExecuteHandle);
     729          18 :   eh->cc = cc;
     730          18 :   eh->cc_cls = cc_cls;
     731          36 :   eh->aaih = TALER_BANK_admin_add_incoming (tc->ctx,
     732          18 :                                             tc->bank_uri,
     733          18 :                                             &tc->auth,
     734             :                                             exchange_base_url,
     735             :                                             &bf.wtid,
     736             :                                             &amount,
     737          18 :                                             (uint64_t) tc->exchange_account_no,
     738             :                                             (uint64_t) account_no,
     739             :                                             &execute_cb,
     740             :                                             eh);
     741          18 :   json_decref (wire);
     742          18 :   if (NULL == eh->aaih)
     743             :   {
     744           0 :     GNUNET_break (0);
     745           0 :     GNUNET_free (eh);
     746           0 :     return NULL;
     747             :   }
     748          18 :   return eh;
     749             : }
     750             : 
     751             : 
     752             : /**
     753             :  * Abort execution of a wire transfer. For example, because we are
     754             :  * shutting down.  Note that if an execution is aborted, it may or
     755             :  * may not still succeed. The caller MUST run @e
     756             :  * execute_wire_transfer again for the same request as soon as
     757             :  * possilbe, to ensure that the request either ultimately succeeds
     758             :  * or ultimately fails. Until this has been done, the transaction is
     759             :  * in limbo (i.e. may or may not have been committed).
     760             :  *
     761             :  * @param cls the @e cls of this struct with the plugin-specific state
     762             :  * @param eh execution to cancel
     763             :  */
     764             : static void
     765           0 : test_execute_wire_transfer_cancel (void *cls,
     766             :                                    struct TALER_WIRE_ExecuteHandle *eh)
     767             : {
     768           0 :   TALER_BANK_admin_add_incoming_cancel (eh->aaih);
     769           0 :   GNUNET_free (eh);
     770           0 : }
     771             : 
     772             : 
     773             : /**
     774             :  * Handle for a #test_get_history() request.
     775             :  */
     776             : struct TALER_WIRE_HistoryHandle
     777             : {
     778             : 
     779             :   /**
     780             :    * Function to call with results.
     781             :    */
     782             :   TALER_WIRE_HistoryResultCallback hres_cb;
     783             : 
     784             :   /**
     785             :    * Closure for @e hres_cb.
     786             :    */
     787             :   void *hres_cb_cls;
     788             : 
     789             :   /**
     790             :    * Request to the bank.
     791             :    */
     792             :   struct TALER_BANK_HistoryHandle *hh;
     793             : 
     794             : };
     795             : 
     796             : 
     797             : /**
     798             :  * Function called with results from the bank about the transaction history.
     799             :  *
     800             :  * @param cls the `struct TALER_WIRE_HistoryHandle`
     801             :  * @param http_status HTTP response code, #MHD_HTTP_OK (200) for successful status request
     802             :  *                    0 if the bank's reply is bogus (fails to follow the protocol),
     803             :  *                    #MHD_HTTP_NO_CONTENT if there are no more results; on success the
     804             :  *                    last callback is always of this status (even if `abs(num_results)` were
     805             :  *                    already returned).
     806             :  * @param dir direction of the transfer
     807             :  * @param serial_id monotonically increasing counter corresponding to the transaction
     808             :  * @param details details about the wire transfer
     809             :  * @param json detailed response from the HTTPD, or NULL if reply was not in JSON
     810             :  */
     811             : static void
     812           3 : bhist_cb (void *cls,
     813             :           unsigned int http_status,
     814             :           enum TALER_BANK_Direction dir,
     815             :           uint64_t serial_id,
     816             :           const struct TALER_BANK_TransferDetails *details,
     817             :           const json_t *json)
     818             : {
     819           3 :   struct TALER_WIRE_HistoryHandle *whh = cls;
     820           3 :   uint64_t bserial_id = GNUNET_htonll (serial_id);
     821             :   struct TALER_WIRE_TransferDetails wd;
     822             : 
     823           3 :   if (MHD_HTTP_OK == http_status)
     824             :   {
     825             :     char *subject;
     826             :     char *space;
     827             : 
     828           1 :     wd.amount = details->amount;
     829           1 :     wd.execution_date = details->execution_date;
     830           1 :     subject = GNUNET_strdup (details->wire_transfer_subject);
     831           1 :     space = strchr (subject, (int) ' ');
     832           1 :     if (NULL != space)
     833             :     {
     834             :       /* Space separates the actual wire transfer subject from the
     835             :          exchange base URL (if present, expected only for outgoing
     836             :          transactions).  So we cut the string off at the space. */
     837           1 :       *space = '\0';
     838             :     }
     839             :     /* NOTE: For a real bank, the subject should include a checksum! */
     840           1 :     if (GNUNET_OK !=
     841           1 :         GNUNET_STRINGS_string_to_data (subject,
     842             :                                        strlen (subject),
     843             :                                        &wd.reserve_pub,
     844             :                                        sizeof (wd.reserve_pub)))
     845             :     {
     846           0 :       GNUNET_break (0);
     847           0 :       GNUNET_free (subject);
     848             :       /* NOTE: for a "real" bank, we would want to trigger logic to undo the
     849             :          wire transfer. However, for the "demo" bank, it should currently
     850             :          be "impossible" to do wire transfers with invalid subjects, and
     851             :          equally we thus don't need to undo them (and there is no API to do
     852             :          that nicely either right now). So we don't handle this case for now. */
     853           0 :       return;
     854             :     }
     855           1 :     GNUNET_free (subject);
     856           1 :     wd.account_details = details->account_details;
     857             : 
     858           2 :     if ( (NULL != whh->hres_cb) &&
     859             :          (GNUNET_OK !=
     860           1 :           whh->hres_cb (whh->hres_cb_cls,
     861             :                         dir,
     862             :                         &bserial_id,
     863             :                         sizeof (bserial_id),
     864             :                         &wd)) )
     865           0 :       whh->hres_cb = NULL;
     866             :   }
     867             :   else
     868             :   {
     869           2 :     if (NULL != whh->hres_cb)
     870           2 :       (void) whh->hres_cb (whh->hres_cb_cls,
     871             :                            TALER_BANK_DIRECTION_NONE,
     872             :                            NULL,
     873             :                            0,
     874             :                            NULL);
     875           2 :     whh->hh = NULL;
     876           2 :     GNUNET_free (whh);
     877             :   }
     878             : }
     879             : 
     880             : 
     881             : /**
     882             :  * Query transfer history of an account.  We use the variable-size
     883             :  * @a start_off to indicate which transfers we are interested in as
     884             :  * different banking systems may have different ways to identify
     885             :  * transfers.  The @a start_off value must thus match the value of
     886             :  * a `row_off` argument previously given to the @a hres_cb.  Use
     887             :  * NULL to query transfers from the beginning of time (with
     888             :  * positive @a num_results) or from the latest committed transfers
     889             :  * (with negative @a num_results).
     890             :  *
     891             :  * @param cls the @e cls of this struct with the plugin-specific state
     892             :  * @param direction what kinds of wire transfers should be returned
     893             :  * @param start_off from which row on do we want to get results, use NULL for the latest; exclusive
     894             :  * @param start_off_len number of bytes in @a start_off; must be `sizeof(uint64_t)`.
     895             :  * @param num_results how many results do we want; negative numbers to go into the past,
     896             :  *                    positive numbers to go into the future starting at @a start_row;
     897             :  *                    must not be zero.
     898             :  * @param hres_cb the callback to call with the transaction history
     899             :  * @param hres_cb_cls closure for the above callback
     900             :  */
     901             : static struct TALER_WIRE_HistoryHandle *
     902           2 : test_get_history (void *cls,
     903             :                   enum TALER_BANK_Direction direction,
     904             :                   const void *start_off,
     905             :                   size_t start_off_len,
     906             :                   int64_t num_results,
     907             :                   TALER_WIRE_HistoryResultCallback hres_cb,
     908             :                   void *hres_cb_cls)
     909             : {
     910           2 :   struct TestClosure *tc = cls;
     911             :   struct TALER_WIRE_HistoryHandle *whh;
     912             :   const uint64_t *start_off_b64;
     913             :   uint64_t start_row;
     914             : 
     915           2 :   if (0 == num_results)
     916             :   {
     917           0 :     GNUNET_break (0);
     918           0 :     return NULL;
     919             :   }
     920           2 :   if (TALER_BANK_DIRECTION_NONE == direction)
     921             :   {
     922           0 :     GNUNET_break (0);
     923           0 :     return NULL;
     924             :   }
     925           2 :   if ( (NULL != start_off) &&
     926             :        (sizeof (uint64_t) != start_off_len) )
     927             :   {
     928           0 :     GNUNET_break (0);
     929           0 :     return NULL;
     930             :   }
     931           2 :   if (NULL == start_off)
     932             :   {
     933           2 :     start_row = UINT64_MAX; /* no start row */
     934             :   }
     935             :   else
     936             :   {
     937           0 :     start_off_b64 = start_off;
     938           0 :     start_row = GNUNET_ntohll (*start_off_b64);
     939             :   }
     940             : 
     941           2 :   whh = GNUNET_new (struct TALER_WIRE_HistoryHandle);
     942           2 :   whh->hres_cb = hres_cb;
     943           2 :   whh->hres_cb_cls = hres_cb_cls;
     944           2 :   whh->hh = TALER_BANK_history (tc->ctx,
     945           2 :                                 tc->bank_uri,
     946           2 :                                 &tc->auth,
     947           2 :                                 (uint64_t) tc->exchange_account_no,
     948             :                                 direction,
     949             :                                 start_row,
     950             :                                 num_results,
     951             :                                 &bhist_cb,
     952             :                                 whh);
     953           2 :   if (NULL == whh->hh)
     954             :   {
     955           0 :     GNUNET_break (0);
     956           0 :     GNUNET_free (whh);
     957           0 :     return NULL;
     958             :   }
     959           2 :   return whh;
     960             : }
     961             : 
     962             : 
     963             : /**
     964             :  * Cancel going over the account's history.
     965             :  *
     966             :  * @param cls the @e cls of this struct with the plugin-specific state
     967             :  * @param whh operation to cancel
     968             :  */
     969             : static void
     970           0 : test_get_history_cancel (void *cls,
     971             :                          struct TALER_WIRE_HistoryHandle *whh)
     972             : {
     973           0 :   TALER_BANK_history_cancel (whh->hh);
     974           0 :   GNUNET_free (whh);
     975           0 : }
     976             : 
     977             : 
     978             : /**
     979             :  * Initialize test-wire subsystem.
     980             :  *
     981             :  * @param cls a configuration instance
     982             :  * @return NULL on error, otherwise a `struct TALER_WIRE_Plugin`
     983             :  */
     984             : void *
     985          25 : libtaler_plugin_wire_test_init (void *cls)
     986             : {
     987          25 :   struct GNUNET_CONFIGURATION_Handle *cfg = cls;
     988             :   struct TestClosure *tc;
     989             :   struct TALER_WIRE_Plugin *plugin;
     990             :   char *user;
     991             :   char *pass;
     992             : 
     993          25 :   tc = GNUNET_new (struct TestClosure);
     994          25 :   if (NULL != cfg)
     995             :   {
     996          23 :     if (GNUNET_OK !=
     997          23 :         GNUNET_CONFIGURATION_get_value_string (cfg,
     998             :                                                "exchange-wire-test",
     999             :                                                "BANK_URI",
    1000             :                                                &tc->bank_uri))
    1001             :     {
    1002           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1003             :                                  "exchange-wire-test",
    1004             :                                  "BANK_URI");
    1005           0 :       GNUNET_free (tc);
    1006           0 :       return NULL;
    1007             :     }
    1008          23 :     if (GNUNET_OK !=
    1009          23 :         GNUNET_CONFIGURATION_get_value_number (cfg,
    1010             :                                                "exchange-wire-test",
    1011             :                                                "EXCHANGE_ACCOUNT_NUMBER",
    1012             :                                                &tc->exchange_account_no))
    1013             :     {
    1014           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1015             :                                  "exchange-wire-test",
    1016             :                                  "EXCHANGE_ACCOUNT_NUMBER");
    1017           0 :       GNUNET_free (tc->bank_uri);
    1018           0 :       GNUNET_free (tc);
    1019           0 :       return NULL;
    1020             :     }
    1021          23 :     if (GNUNET_OK !=
    1022          23 :         GNUNET_CONFIGURATION_get_value_string (cfg,
    1023             :                                                "taler",
    1024             :                                                "CURRENCY",
    1025             :                                                &tc->currency))
    1026             :     {
    1027           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1028             :                                  "taler",
    1029             :                                  "CURRENCY");
    1030           0 :       GNUNET_free (tc->bank_uri);
    1031           0 :       GNUNET_free (tc);
    1032           0 :       return NULL;
    1033             :     }
    1034          23 :     if (GNUNET_OK !=
    1035          23 :         GNUNET_CONFIGURATION_get_value_string (cfg,
    1036             :                                                "exchange-wire-test",
    1037             :                                                "USERNAME",
    1038             :                                                &user))
    1039             :     {
    1040           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1041             :                                  "exchange-wire-test",
    1042             :                                  "USERNAME");
    1043           0 :       GNUNET_free (tc->bank_uri);
    1044           0 :       GNUNET_free (tc);
    1045           0 :       return NULL;
    1046             :     }
    1047          23 :     if (GNUNET_OK !=
    1048          23 :         GNUNET_CONFIGURATION_get_value_string (cfg,
    1049             :                                                "exchange-wire-test",
    1050             :                                                "PASSWORD",
    1051             :                                                &pass))
    1052             :     {
    1053           0 :       GNUNET_log_config_missing (GNUNET_ERROR_TYPE_ERROR,
    1054             :                                  "exchange-wire-test",
    1055             :                                  "PASSWORD");
    1056           0 :       GNUNET_free (tc->bank_uri);
    1057           0 :       GNUNET_free (tc);
    1058           0 :       GNUNET_free (user);
    1059           0 :       return NULL;
    1060             :     }
    1061          23 :     tc->auth.method = TALER_BANK_AUTH_BASIC;
    1062          23 :     tc->auth.details.basic.username = user;
    1063          23 :     tc->auth.details.basic.password = pass;
    1064          23 :     tc->ctx = GNUNET_CURL_init (&GNUNET_CURL_gnunet_scheduler_reschedule,
    1065          23 :                                 &tc->rc);
    1066          23 :     tc->rc = GNUNET_CURL_gnunet_rc_create (tc->ctx);
    1067          23 :     if (NULL == tc->ctx)
    1068             :     {
    1069           0 :       GNUNET_break (0);
    1070           0 :       GNUNET_free (tc->currency);
    1071           0 :       GNUNET_free (tc->bank_uri);
    1072           0 :       GNUNET_free (tc->auth.details.basic.username);
    1073           0 :       GNUNET_free (tc->auth.details.basic.password);
    1074           0 :       GNUNET_free (tc);
    1075           0 :       return NULL;
    1076             :     }
    1077             :   }
    1078          25 :   plugin = GNUNET_new (struct TALER_WIRE_Plugin);
    1079          25 :   plugin->cls = tc;
    1080          25 :   plugin->amount_round = &test_amount_round;
    1081          25 :   plugin->get_wire_details = &test_get_wire_details;
    1082          25 :   plugin->sign_wire_details = &test_sign_wire_details;
    1083          25 :   plugin->wire_validate = &test_wire_validate;
    1084          25 :   plugin->prepare_wire_transfer = &test_prepare_wire_transfer;
    1085          25 :   plugin->prepare_wire_transfer_cancel = &test_prepare_wire_transfer_cancel;
    1086          25 :   plugin->execute_wire_transfer = &test_execute_wire_transfer;
    1087          25 :   plugin->execute_wire_transfer_cancel = &test_execute_wire_transfer_cancel;
    1088          25 :   plugin->get_history = &test_get_history;
    1089          25 :   plugin->get_history_cancel = &test_get_history_cancel;
    1090          25 :   return plugin;
    1091             : }
    1092             : 
    1093             : 
    1094             : /**
    1095             :  * Shutdown Test wire subsystem.
    1096             :  *
    1097             :  * @param cls a `struct TALER_WIRE_Plugin`
    1098             :  * @return NULL (always)
    1099             :  */
    1100             : void *
    1101          24 : libtaler_plugin_wire_test_done (void *cls)
    1102             : {
    1103          24 :   struct TALER_WIRE_Plugin *plugin = cls;
    1104          24 :   struct TestClosure *tc = plugin->cls;
    1105             : 
    1106          24 :   if (NULL != tc->ctx)
    1107             :   {
    1108          22 :     GNUNET_CURL_fini (tc->ctx);
    1109          22 :     tc->ctx = NULL;
    1110             :   }
    1111          24 :   if (NULL != tc->rc)
    1112             :   {
    1113          22 :     GNUNET_CURL_gnunet_rc_destroy (tc->rc);
    1114          22 :     tc->rc = NULL;
    1115             :   }
    1116          24 :   switch (tc->auth.method)
    1117             :   {
    1118             :   case TALER_BANK_AUTH_NONE:
    1119           2 :     break;
    1120             :   case TALER_BANK_AUTH_BASIC:
    1121          22 :     if (NULL != tc->auth.details.basic.username)
    1122             :     {
    1123          22 :       GNUNET_free (tc->auth.details.basic.username);
    1124          22 :       tc->auth.details.basic.username = NULL;
    1125             :     }
    1126          22 :     if (NULL != tc->auth.details.basic.password)
    1127             :     {
    1128          22 :       GNUNET_free (tc->auth.details.basic.password);
    1129          22 :       tc->auth.details.basic.password = NULL;
    1130             :     }
    1131          22 :     break;
    1132             :   }
    1133          24 :   GNUNET_free_non_null (tc->currency);
    1134          24 :   GNUNET_free_non_null (tc->bank_uri);
    1135          24 :   GNUNET_free (tc);
    1136          24 :   GNUNET_free (plugin);
    1137          24 :   return NULL;
    1138             : }
    1139             : 
    1140             : /* end of plugin_wire_test.c */

Generated by: LCOV version 1.13