exchange

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

bank_api_transfer.c (9689B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2015--2023 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
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file bank-lib/bank_api_transfer.c
     19  * @brief Implementation of the /transfer/ requests of the bank's HTTP API
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include "bank_api_common.h"
     24 #include <microhttpd.h> /* just for HTTP status codes */
     25 #include "taler/taler_signatures.h"
     26 #include "taler/taler_curl_lib.h"
     27 #include "taler/taler_bank_service.h"
     28 
     29 
     30 GNUNET_NETWORK_STRUCT_BEGIN
     31 
     32 /**
     33  * Data structure serialized in the prepare stage.
     34  */
     35 struct WirePackP
     36 {
     37   /**
     38    * Random unique identifier for the request.
     39    */
     40   struct GNUNET_HashCode request_uid;
     41 
     42   /**
     43    * Amount to be transferred.
     44    */
     45   struct TALER_AmountNBO amount;
     46 
     47   /**
     48    * Wire transfer identifier to use.
     49    */
     50   struct TALER_WireTransferIdentifierRawP wtid;
     51 
     52   /**
     53    * Length of the payto:// URL of the target account,
     54    * including 0-terminator, in network byte order.
     55    */
     56   uint32_t account_len GNUNET_PACKED;
     57 
     58   /**
     59    * Length of the exchange's base URL,
     60    * including 0-terminator, in network byte order.
     61    */
     62   uint32_t exchange_url_len GNUNET_PACKED;
     63 
     64 };
     65 
     66 GNUNET_NETWORK_STRUCT_END
     67 
     68 
     69 void
     70 TALER_BANK_prepare_transfer (
     71   const struct TALER_FullPayto destination_account_payto_uri,
     72   const struct TALER_Amount *amount,
     73   const char *exchange_base_url,
     74   const struct TALER_WireTransferIdentifierRawP *wtid,
     75   void **buf,
     76   size_t *buf_size)
     77 {
     78   const char *payto = destination_account_payto_uri.full_payto;
     79   struct WirePackP *wp;
     80   size_t d_len = strlen (payto) + 1;
     81   size_t u_len = strlen (exchange_base_url) + 1;
     82   char *end;
     83 
     84   if ( (d_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) ||
     85        (u_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) ||
     86        (d_len + u_len + sizeof (*wp) >= GNUNET_MAX_MALLOC_CHECKED) )
     87   {
     88     GNUNET_break (0); /* that's some long URL... */
     89     *buf = NULL;
     90     *buf_size = 0;
     91     return;
     92   }
     93   *buf_size = sizeof (*wp) + d_len + u_len;
     94   wp = GNUNET_malloc (*buf_size);
     95   GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
     96                                     &wp->request_uid);
     97   TALER_amount_hton (&wp->amount,
     98                      amount);
     99   wp->wtid = *wtid;
    100   wp->account_len = htonl ((uint32_t) d_len);
    101   wp->exchange_url_len = htonl ((uint32_t) u_len);
    102   end = (char *) &wp[1];
    103   GNUNET_memcpy (end,
    104                  payto,
    105                  d_len);
    106   GNUNET_memcpy (end + d_len,
    107                  exchange_base_url,
    108                  u_len);
    109   *buf = (char *) wp;
    110 }
    111 
    112 
    113 /**
    114  * @brief Handle for an active wire transfer.
    115  */
    116 struct TALER_BANK_TransferHandle
    117 {
    118 
    119   /**
    120    * The url for this request.
    121    */
    122   char *request_url;
    123 
    124   /**
    125    * POST context.
    126    */
    127   struct TALER_CURL_PostContext post_ctx;
    128 
    129   /**
    130    * Handle for the request.
    131    */
    132   struct GNUNET_CURL_Job *job;
    133 
    134   /**
    135    * Function to call with the result.
    136    */
    137   TALER_BANK_TransferCallback cb;
    138 
    139   /**
    140    * Closure for @a cb.
    141    */
    142   void *cb_cls;
    143 
    144 };
    145 
    146 
    147 /**
    148  * Function called when we're done processing the
    149  * HTTP /transfer request.
    150  *
    151  * @param cls the `struct TALER_BANK_TransferHandle`
    152  * @param response_code HTTP response code, 0 on error
    153  * @param response parsed JSON result, NULL on error
    154  */
    155 static void
    156 handle_transfer_finished (void *cls,
    157                           long response_code,
    158                           const void *response)
    159 {
    160   struct TALER_BANK_TransferHandle *th = cls;
    161   const json_t *j = response;
    162   struct TALER_BANK_TransferResponse tr = {
    163     .http_status = response_code,
    164     .response = j
    165   };
    166 
    167   th->job = NULL;
    168   switch (response_code)
    169   {
    170   case 0:
    171     tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    172     break;
    173   case MHD_HTTP_OK:
    174     {
    175       struct GNUNET_JSON_Specification spec[] = {
    176         GNUNET_JSON_spec_uint64 ("row_id",
    177                                  &tr.details.ok.row_id),
    178         GNUNET_JSON_spec_timestamp ("timestamp",
    179                                     &tr.details.ok.timestamp),
    180         GNUNET_JSON_spec_end ()
    181       };
    182 
    183       if (GNUNET_OK !=
    184           GNUNET_JSON_parse (j,
    185                              spec,
    186                              NULL, NULL))
    187       {
    188         GNUNET_break_op (0);
    189         tr.http_status = 0;
    190         tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    191         break;
    192       }
    193     }
    194     break;
    195   case MHD_HTTP_BAD_REQUEST:
    196     /* This should never happen, either us or the bank is buggy
    197        (or API version conflict); just pass JSON reply to the application */
    198     GNUNET_break_op (0);
    199     tr.ec = TALER_JSON_get_error_code (j);
    200     break;
    201   case MHD_HTTP_UNAUTHORIZED:
    202     /* Nothing really to verify, bank says our credentials are
    203        invalid. We should pass the JSON reply to the application. */
    204     tr.ec = TALER_JSON_get_error_code (j);
    205     break;
    206   case MHD_HTTP_NOT_FOUND:
    207     /* Nothing really to verify, endpoint wrong -- could be user unknown */
    208     tr.ec = TALER_JSON_get_error_code (j);
    209     break;
    210   case MHD_HTTP_CONFLICT:
    211     /* Nothing really to verify. Server says we used the same transfer request
    212        UID before, but with different details.  Should not happen if the user
    213        properly used #TALER_BANK_prepare_transfer() and our PRNG is not
    214        broken... */
    215     tr.ec = TALER_JSON_get_error_code (j);
    216     break;
    217   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    218     /* Server had an internal issue; we should retry, but this API
    219        leaves this to the application */
    220     tr.ec = TALER_JSON_get_error_code (j);
    221     break;
    222   default:
    223     /* unexpected response code */
    224     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    225                 "Unexpected response code %u\n",
    226                 (unsigned int) response_code);
    227     GNUNET_break (0);
    228     tr.ec = TALER_JSON_get_error_code (j);
    229     break;
    230   }
    231   th->cb (th->cb_cls,
    232           &tr);
    233   TALER_BANK_transfer_cancel (th);
    234 }
    235 
    236 
    237 struct TALER_BANK_TransferHandle *
    238 TALER_BANK_transfer (
    239   struct GNUNET_CURL_Context *ctx,
    240   const struct TALER_BANK_AuthenticationData *auth,
    241   const void *buf,
    242   size_t buf_size,
    243   TALER_BANK_TransferCallback cc,
    244   void *cc_cls)
    245 {
    246   struct TALER_BANK_TransferHandle *th;
    247   json_t *transfer_obj;
    248   CURL *eh;
    249   const struct WirePackP *wp = buf;
    250   uint32_t d_len;
    251   uint32_t u_len;
    252   const char *destination_account_uri;
    253   const char *exchange_base_url;
    254   struct TALER_Amount amount;
    255 
    256   if (sizeof (*wp) > buf_size)
    257   {
    258     GNUNET_break (0);
    259     return NULL;
    260   }
    261   d_len = ntohl (wp->account_len);
    262   u_len = ntohl (wp->exchange_url_len);
    263   if ( (sizeof (*wp) + d_len + u_len != buf_size) ||
    264        (d_len > buf_size) ||
    265        (u_len > buf_size) ||
    266        (d_len + u_len > buf_size) )
    267   {
    268     GNUNET_break (0);
    269     return NULL;
    270   }
    271   destination_account_uri = (const char *) &wp[1];
    272   exchange_base_url = destination_account_uri + d_len;
    273   if ( ('\0' != destination_account_uri[d_len - 1]) ||
    274        ('\0' != exchange_base_url[u_len - 1]) )
    275   {
    276     GNUNET_break (0);
    277     return NULL;
    278   }
    279   if (NULL == auth->wire_gateway_url)
    280   {
    281     GNUNET_break (0);
    282     return NULL;
    283   }
    284   TALER_amount_ntoh (&amount,
    285                      &wp->amount);
    286   th = GNUNET_new (struct TALER_BANK_TransferHandle);
    287   th->cb = cc;
    288   th->cb_cls = cc_cls;
    289   th->request_url = TALER_url_join (auth->wire_gateway_url,
    290                                     "transfer",
    291                                     NULL);
    292   if (NULL == th->request_url)
    293   {
    294     GNUNET_free (th);
    295     GNUNET_break (0);
    296     return NULL;
    297   }
    298   transfer_obj = GNUNET_JSON_PACK (
    299     GNUNET_JSON_pack_data_auto ("request_uid",
    300                                 &wp->request_uid),
    301     TALER_JSON_pack_amount ("amount",
    302                             &amount),
    303     GNUNET_JSON_pack_string ("exchange_base_url",
    304                              exchange_base_url),
    305     GNUNET_JSON_pack_data_auto ("wtid",
    306                                 &wp->wtid),
    307     GNUNET_JSON_pack_string ("credit_account",
    308                              destination_account_uri));
    309   if (NULL == transfer_obj)
    310   {
    311     GNUNET_break (0);
    312     return NULL;
    313   }
    314   eh = curl_easy_init ();
    315   if ( (NULL == eh) ||
    316        (GNUNET_OK !=
    317         TALER_BANK_setup_auth_ (eh,
    318                                 auth)) ||
    319        (CURLE_OK !=
    320         curl_easy_setopt (eh,
    321                           CURLOPT_URL,
    322                           th->request_url)) ||
    323        (GNUNET_OK !=
    324         TALER_curl_easy_post (&th->post_ctx,
    325                               eh,
    326                               transfer_obj)) )
    327   {
    328     GNUNET_break (0);
    329     TALER_BANK_transfer_cancel (th);
    330     if (NULL != eh)
    331       curl_easy_cleanup (eh);
    332     json_decref (transfer_obj);
    333     return NULL;
    334   }
    335   json_decref (transfer_obj);
    336   th->job = GNUNET_CURL_job_add2 (ctx,
    337                                   eh,
    338                                   th->post_ctx.headers,
    339                                   &handle_transfer_finished,
    340                                   th);
    341   return th;
    342 }
    343 
    344 
    345 void
    346 TALER_BANK_transfer_cancel (struct TALER_BANK_TransferHandle *th)
    347 {
    348   if (NULL != th->job)
    349   {
    350     GNUNET_CURL_job_cancel (th->job);
    351     th->job = NULL;
    352   }
    353   TALER_curl_easy_post_finished (&th->post_ctx);
    354   GNUNET_free (th->request_url);
    355   GNUNET_free (th);
    356 }
    357 
    358 
    359 /* end of bank_api_transfer.c */