LCOV - code coverage report
Current view: top level - bank-lib - bank_api_transfer.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 73 126 57.9 %
Date: 2025-06-22 12:09:43 Functions: 4 4 100.0 %

          Line data    Source code
       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          76 : 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          76 :   const char *payto = destination_account_payto_uri.full_payto;
      79             :   struct WirePackP *wp;
      80          76 :   size_t d_len = strlen (payto) + 1;
      81          76 :   size_t u_len = strlen (exchange_base_url) + 1;
      82             :   char *end;
      83             : 
      84          76 :   if ( (d_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) ||
      85          76 :        (u_len >= (size_t) GNUNET_MAX_MALLOC_CHECKED) ||
      86          76 :        (d_len + u_len + sizeof (*wp) >= GNUNET_MAX_MALLOC_CHECKED) )
      87             :   {
      88           0 :     GNUNET_break (0); /* that's some long URL... */
      89           0 :     *buf = NULL;
      90           0 :     *buf_size = 0;
      91           0 :     return;
      92             :   }
      93          76 :   *buf_size = sizeof (*wp) + d_len + u_len;
      94          76 :   wp = GNUNET_malloc (*buf_size);
      95          76 :   GNUNET_CRYPTO_hash_create_random (GNUNET_CRYPTO_QUALITY_NONCE,
      96             :                                     &wp->request_uid);
      97          76 :   TALER_amount_hton (&wp->amount,
      98             :                      amount);
      99          76 :   wp->wtid = *wtid;
     100          76 :   wp->account_len = htonl ((uint32_t) d_len);
     101          76 :   wp->exchange_url_len = htonl ((uint32_t) u_len);
     102          76 :   end = (char *) &wp[1];
     103          76 :   GNUNET_memcpy (end,
     104             :                  payto,
     105             :                  d_len);
     106          76 :   GNUNET_memcpy (end + d_len,
     107             :                  exchange_base_url,
     108             :                  u_len);
     109          76 :   *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          70 : handle_transfer_finished (void *cls,
     157             :                           long response_code,
     158             :                           const void *response)
     159             : {
     160          70 :   struct TALER_BANK_TransferHandle *th = cls;
     161          70 :   const json_t *j = response;
     162          70 :   struct TALER_BANK_TransferResponse tr = {
     163             :     .http_status = response_code,
     164             :     .response = j
     165             :   };
     166             : 
     167          70 :   th->job = NULL;
     168          70 :   switch (response_code)
     169             :   {
     170           0 :   case 0:
     171           0 :     tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     172           0 :     break;
     173          70 :   case MHD_HTTP_OK:
     174             :     {
     175             :       struct GNUNET_JSON_Specification spec[] = {
     176          70 :         GNUNET_JSON_spec_uint64 ("row_id",
     177             :                                  &tr.details.ok.row_id),
     178          70 :         GNUNET_JSON_spec_timestamp ("timestamp",
     179             :                                     &tr.details.ok.timestamp),
     180          70 :         GNUNET_JSON_spec_end ()
     181             :       };
     182             : 
     183          70 :       if (GNUNET_OK !=
     184          70 :           GNUNET_JSON_parse (j,
     185             :                              spec,
     186             :                              NULL, NULL))
     187             :       {
     188           0 :         GNUNET_break_op (0);
     189           0 :         tr.http_status = 0;
     190           0 :         tr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     191           0 :         break;
     192             :       }
     193             :     }
     194          70 :     break;
     195           0 :   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           0 :     GNUNET_break_op (0);
     199           0 :     tr.ec = TALER_JSON_get_error_code (j);
     200           0 :     break;
     201           0 :   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           0 :     tr.ec = TALER_JSON_get_error_code (j);
     205           0 :     break;
     206           0 :   case MHD_HTTP_NOT_FOUND:
     207             :     /* Nothing really to verify, endpoint wrong -- could be user unknown */
     208           0 :     tr.ec = TALER_JSON_get_error_code (j);
     209           0 :     break;
     210           0 :   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           0 :     tr.ec = TALER_JSON_get_error_code (j);
     216           0 :     break;
     217           0 :   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           0 :     tr.ec = TALER_JSON_get_error_code (j);
     221           0 :     break;
     222           0 :   default:
     223             :     /* unexpected response code */
     224           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     225             :                 "Unexpected response code %u\n",
     226             :                 (unsigned int) response_code);
     227           0 :     GNUNET_break (0);
     228           0 :     tr.ec = TALER_JSON_get_error_code (j);
     229           0 :     break;
     230             :   }
     231          70 :   th->cb (th->cb_cls,
     232             :           &tr);
     233          70 :   TALER_BANK_transfer_cancel (th);
     234          70 : }
     235             : 
     236             : 
     237             : struct TALER_BANK_TransferHandle *
     238          70 : 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          70 :   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          70 :   if (sizeof (*wp) > buf_size)
     257             :   {
     258           0 :     GNUNET_break (0);
     259           0 :     return NULL;
     260             :   }
     261          70 :   d_len = ntohl (wp->account_len);
     262          70 :   u_len = ntohl (wp->exchange_url_len);
     263          70 :   if ( (sizeof (*wp) + d_len + u_len != buf_size) ||
     264          70 :        (d_len > buf_size) ||
     265          70 :        (u_len > buf_size) ||
     266          70 :        (d_len + u_len > buf_size) )
     267             :   {
     268           0 :     GNUNET_break (0);
     269           0 :     return NULL;
     270             :   }
     271          70 :   destination_account_uri = (const char *) &wp[1];
     272          70 :   exchange_base_url = destination_account_uri + d_len;
     273          70 :   if ( ('\0' != destination_account_uri[d_len - 1]) ||
     274          70 :        ('\0' != exchange_base_url[u_len - 1]) )
     275             :   {
     276           0 :     GNUNET_break (0);
     277           0 :     return NULL;
     278             :   }
     279          70 :   if (NULL == auth->wire_gateway_url)
     280             :   {
     281           0 :     GNUNET_break (0);
     282           0 :     return NULL;
     283             :   }
     284          70 :   TALER_amount_ntoh (&amount,
     285             :                      &wp->amount);
     286          70 :   th = GNUNET_new (struct TALER_BANK_TransferHandle);
     287          70 :   th->cb = cc;
     288          70 :   th->cb_cls = cc_cls;
     289          70 :   th->request_url = TALER_url_join (auth->wire_gateway_url,
     290             :                                     "transfer",
     291             :                                     NULL);
     292          70 :   if (NULL == th->request_url)
     293             :   {
     294           0 :     GNUNET_free (th);
     295           0 :     GNUNET_break (0);
     296           0 :     return NULL;
     297             :   }
     298          70 :   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          70 :   if (NULL == transfer_obj)
     310             :   {
     311           0 :     GNUNET_break (0);
     312           0 :     return NULL;
     313             :   }
     314          70 :   eh = curl_easy_init ();
     315         140 :   if ( (NULL == eh) ||
     316             :        (GNUNET_OK !=
     317          70 :         TALER_BANK_setup_auth_ (eh,
     318          70 :                                 auth)) ||
     319             :        (CURLE_OK !=
     320          70 :         curl_easy_setopt (eh,
     321             :                           CURLOPT_URL,
     322          70 :                           th->request_url)) ||
     323             :        (GNUNET_OK !=
     324          70 :         TALER_curl_easy_post (&th->post_ctx,
     325             :                               eh,
     326             :                               transfer_obj)) )
     327             :   {
     328           0 :     GNUNET_break (0);
     329           0 :     TALER_BANK_transfer_cancel (th);
     330           0 :     if (NULL != eh)
     331           0 :       curl_easy_cleanup (eh);
     332           0 :     json_decref (transfer_obj);
     333           0 :     return NULL;
     334             :   }
     335          70 :   json_decref (transfer_obj);
     336         140 :   th->job = GNUNET_CURL_job_add2 (ctx,
     337             :                                   eh,
     338          70 :                                   th->post_ctx.headers,
     339             :                                   &handle_transfer_finished,
     340             :                                   th);
     341          70 :   return th;
     342             : }
     343             : 
     344             : 
     345             : void
     346          70 : TALER_BANK_transfer_cancel (struct TALER_BANK_TransferHandle *th)
     347             : {
     348          70 :   if (NULL != th->job)
     349             :   {
     350           0 :     GNUNET_CURL_job_cancel (th->job);
     351           0 :     th->job = NULL;
     352             :   }
     353          70 :   TALER_curl_easy_post_finished (&th->post_ctx);
     354          70 :   GNUNET_free (th->request_url);
     355          70 :   GNUNET_free (th);
     356          70 : }
     357             : 
     358             : 
     359             : /* end of bank_api_transfer.c */

Generated by: LCOV version 1.16