LCOV - code coverage report
Current view: top level - bank-lib - bank_api_debit.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 59.5 % 131 78
Test Date: 2026-10-17 15:46:49 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /*
       2              :   This file is part of TALER
       3              :   Copyright (C) 2017--2023 Taler Systems SA
       4              : 
       5              :   TALER is free software; you can redistribute it and/or
       6              :   modify it under the terms of the GNU General Public License
       7              :   as published by the Free Software Foundation; either version 3,
       8              :   or (at your option) any later version.
       9              : 
      10              :   TALER is distributed in the hope that it will be useful,
      11              :   but WITHOUT ANY WARRANTY; without even the implied warranty of
      12              :   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      13              :   GNU General Public License for more details.
      14              : 
      15              :   You should have received a copy of the GNU General Public
      16              :   License along with TALER; see the file COPYING.  If not,
      17              :   see <http://www.gnu.org/licenses/>
      18              : */
      19              : /**
      20              :  * @file bank-lib/bank_api_debit.c
      21              :  * @brief Implementation of the /history/outgoing
      22              :  *        requests of the bank's HTTP API.
      23              :  * @author Christian Grothoff
      24              :  * @author Marcello Stanisci
      25              :  */
      26              : #include "bank_api_common.h"
      27              : #include <microhttpd.h> /* just for HTTP status codes */
      28              : #include "taler/taler_signatures.h"
      29              : 
      30              : 
      31              : /**
      32              :  * How much longer than the application-specified timeout
      33              :  * do we wait (giving the server a chance to respond)?
      34              :  */
      35              : #define GRACE_PERIOD_MS 1000
      36              : 
      37              : 
      38              : /**
      39              :  * @brief A /history/outgoing Handle
      40              :  */
      41              : struct TALER_BANK_DebitHistoryHandle
      42              : {
      43              : 
      44              :   /**
      45              :    * The url for this request.
      46              :    */
      47              :   char *request_url;
      48              : 
      49              :   /**
      50              :    * Handle for the request.
      51              :    */
      52              :   struct GNUNET_CURL_Job *job;
      53              : 
      54              :   /**
      55              :    * Function to call with the result.
      56              :    */
      57              :   TALER_BANK_DebitHistoryCallback hcb;
      58              : 
      59              :   /**
      60              :    * Closure for @a cb.
      61              :    */
      62              :   void *hcb_cls;
      63              : };
      64              : 
      65              : 
      66              : /**
      67              :  * Parse history given in JSON format and invoke the callback on each item.
      68              :  *
      69              :  * @param hh handle to the account history request
      70              :  * @param history JSON array with the history
      71              :  * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
      72              :  *         were set,
      73              :  *         #GNUNET_SYSERR if there was a protocol violation in @a history
      74              :  */
      75              : static enum GNUNET_GenericReturnValue
      76            5 : parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
      77              :                        const json_t *history)
      78              : {
      79            5 :   struct TALER_BANK_DebitHistoryResponse dhr = {
      80              :     .http_status = MHD_HTTP_OK,
      81              :     .ec = TALER_EC_NONE,
      82              :     .response = history
      83              :   };
      84              :   const json_t *history_array;
      85              :   struct GNUNET_JSON_Specification spec[] = {
      86            5 :     GNUNET_JSON_spec_array_const ("outgoing_transactions",
      87              :                                   &history_array),
      88            5 :     TALER_JSON_spec_full_payto_uri ("debit_account",
      89              :                                     &dhr.details.ok.debit_account_uri),
      90            5 :     GNUNET_JSON_spec_end ()
      91              :   };
      92              : 
      93            5 :   if (GNUNET_OK !=
      94            5 :       GNUNET_JSON_parse (history,
      95              :                          spec,
      96              :                          NULL,
      97              :                          NULL))
      98              :   {
      99            0 :     GNUNET_break_op (0);
     100            0 :     return GNUNET_SYSERR;
     101              :   }
     102              :   {
     103            5 :     size_t len = json_array_size (history_array);
     104              :     struct TALER_BANK_DebitDetails *dd;
     105              : 
     106            5 :     GNUNET_break_op (0 != len);
     107            5 :     dd = GNUNET_new_array (GNUNET_NZL (len),
     108              :                            struct TALER_BANK_DebitDetails);
     109           38 :     for (unsigned int i = 0; i<len; i++)
     110              :     {
     111           33 :       struct TALER_BANK_DebitDetails *td = &dd[i];
     112              :       bool no_debit_fee;
     113              :       struct GNUNET_JSON_Specification hist_spec[] = {
     114           33 :         TALER_JSON_spec_amount_any ("amount",
     115              :                                     &td->amount),
     116           33 :         GNUNET_JSON_spec_mark_optional (
     117              :           TALER_JSON_spec_amount_any ("debit_fee",
     118              :                                       &td->debit_fee),
     119              :           &no_debit_fee),
     120           33 :         GNUNET_JSON_spec_timestamp ("date",
     121              :                                     &td->execution_date),
     122           33 :         GNUNET_JSON_spec_uint64 ("row_id",
     123              :                                  &td->serial_id),
     124           33 :         GNUNET_JSON_spec_fixed_auto ("wtid",
     125              :                                      &td->wtid),
     126           33 :         TALER_JSON_spec_full_payto_uri ("credit_account",
     127              :                                         &td->credit_account_uri),
     128           33 :         TALER_JSON_spec_web_url ("exchange_base_url",
     129              :                                  &td->exchange_base_url),
     130           33 :         GNUNET_JSON_spec_mark_optional (
     131              :           GNUNET_JSON_spec_string ("metadata",
     132              :                                    &td->metadata),
     133              :           NULL),
     134           33 :         GNUNET_JSON_spec_end ()
     135              :       };
     136           33 :       json_t *transaction = json_array_get (history_array,
     137              :                                             i);
     138              : 
     139           33 :       if (GNUNET_OK !=
     140           33 :           GNUNET_JSON_parse (transaction,
     141              :                              hist_spec,
     142              :                              NULL,
     143              :                              NULL))
     144              :       {
     145            0 :         GNUNET_break_op (0);
     146            0 :         GNUNET_free (dd);
     147            0 :         return GNUNET_SYSERR;
     148              :       }
     149           33 :       if (no_debit_fee)
     150              :       {
     151           33 :         GNUNET_assert (GNUNET_OK ==
     152              :                        TALER_amount_set_zero (td->amount.currency,
     153              :                                               &td->debit_fee));
     154              :       }
     155            0 :       else if (GNUNET_YES !=
     156            0 :                TALER_amount_cmp_currency (&td->amount,
     157            0 :                                           &td->debit_fee))
     158              :       {
     159            0 :         GNUNET_break_op (0);
     160            0 :         GNUNET_free (dd);
     161            0 :         return GNUNET_SYSERR;
     162              :       }
     163              :     }
     164            5 :     dhr.details.ok.details_length = len;
     165            5 :     dhr.details.ok.details = dd;
     166            5 :     hh->hcb (hh->hcb_cls,
     167              :              &dhr);
     168            5 :     GNUNET_free (dd);
     169              :   }
     170            5 :   return GNUNET_OK;
     171              : }
     172              : 
     173              : 
     174              : /**
     175              :  * Function called when we're done processing the
     176              :  * HTTP /history/outgoing request.
     177              :  *
     178              :  * @param cls the `struct TALER_BANK_DebitHistoryHandle`
     179              :  * @param response_code HTTP response code, 0 on error
     180              :  * @param response parsed JSON result, NULL on error
     181              :  */
     182              : static void
     183           11 : handle_debit_history_finished (void *cls,
     184              :                                long response_code,
     185              :                                const void *response)
     186              : {
     187           11 :   struct TALER_BANK_DebitHistoryHandle *hh = cls;
     188           11 :   struct TALER_BANK_DebitHistoryResponse dhr = {
     189              :     .http_status = response_code,
     190              :     .response = response
     191              :   };
     192              : 
     193           11 :   hh->job = NULL;
     194           11 :   switch (response_code)
     195              :   {
     196            0 :   case 0:
     197            0 :     dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     198            0 :     break;
     199            5 :   case MHD_HTTP_OK:
     200            5 :     if (GNUNET_OK !=
     201            5 :         parse_account_history (hh,
     202              :                                dhr.response))
     203              :     {
     204            0 :       GNUNET_break_op (0);
     205            0 :       dhr.http_status = 0;
     206            0 :       dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     207            0 :       break;
     208              :     }
     209            5 :     TALER_BANK_debit_history_cancel (hh);
     210            5 :     return;
     211            4 :   case MHD_HTTP_NO_CONTENT:
     212            4 :     break;
     213            0 :   case MHD_HTTP_BAD_REQUEST:
     214              :     /* This should never happen, either us or the bank is buggy
     215              :        (or API version conflict); just pass JSON reply to the application */
     216            0 :     GNUNET_break_op (0);
     217            0 :     dhr.ec = TALER_JSON_get_error_code (dhr.response);
     218            0 :     break;
     219            0 :   case MHD_HTTP_UNAUTHORIZED:
     220              :     /* Nothing really to verify, bank says the HTTP Authentication
     221              :        failed. May happen if HTTP authentication is used and the
     222              :        user supplied a wrong username/password combination. */
     223            0 :     dhr.ec = TALER_JSON_get_error_code (dhr.response);
     224            0 :     break;
     225            2 :   case MHD_HTTP_NOT_FOUND:
     226              :     /* Nothing really to verify: the bank is either unaware
     227              :        of the endpoint (not a bank), or of the account.
     228              :        We should pass the JSON (?) reply to the application */
     229            2 :     dhr.ec = TALER_JSON_get_error_code (dhr.response);
     230            2 :     break;
     231            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     232              :     /* Server had an internal issue; we should retry, but this API
     233              :        leaves this to the application */
     234            0 :     dhr.ec = TALER_JSON_get_error_code (dhr.response);
     235            0 :     break;
     236            0 :   default:
     237              :     /* unexpected response code */
     238            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     239              :                 "Unexpected response code %u\n",
     240              :                 (unsigned int) response_code);
     241            0 :     dhr.ec = TALER_JSON_get_error_code (dhr.response);
     242            0 :     break;
     243              :   }
     244            6 :   hh->hcb (hh->hcb_cls,
     245              :            &dhr);
     246            6 :   TALER_BANK_debit_history_cancel (hh);
     247              : }
     248              : 
     249              : 
     250              : struct TALER_BANK_DebitHistoryHandle *
     251           11 : TALER_BANK_debit_history (struct GNUNET_CURL_Context *ctx,
     252              :                           const struct TALER_BANK_AuthenticationData *auth,
     253              :                           uint64_t start_row,
     254              :                           int64_t num_results,
     255              :                           struct GNUNET_TIME_Relative timeout,
     256              :                           TALER_BANK_DebitHistoryCallback hres_cb,
     257              :                           void *hres_cb_cls)
     258              : {
     259              :   char url[128];
     260              :   struct TALER_BANK_DebitHistoryHandle *hh;
     261              :   CURL *eh;
     262              :   unsigned long long tms;
     263              : 
     264           11 :   if (0 == num_results)
     265              :   {
     266            0 :     GNUNET_break (0);
     267            0 :     return NULL;
     268              :   }
     269              : 
     270           22 :   tms = (unsigned long long) (timeout.rel_value_us
     271           11 :                               / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
     272           11 :   if ( ( (UINT64_MAX == start_row) &&
     273           10 :          (0 > num_results) ) ||
     274           10 :        ( (0 == start_row) &&
     275              :          (0 < num_results) ) )
     276              :   {
     277           11 :     if ( (0 < num_results) &&
     278           10 :          (! GNUNET_TIME_relative_is_zero (timeout)) )
     279            0 :       GNUNET_snprintf (url,
     280              :                        sizeof (url),
     281              :                        "history/outgoing?limit=%lld&timeout_ms=%llu",
     282              :                        (long long) num_results,
     283              :                        tms);
     284              :     else
     285           11 :       GNUNET_snprintf (url,
     286              :                        sizeof (url),
     287              :                        "history/outgoing?limit=%lld",
     288              :                        (long long) num_results);
     289              :   }
     290              :   else
     291              :   {
     292            0 :     if ( (0 < num_results) &&
     293            0 :          (! GNUNET_TIME_relative_is_zero (timeout)) )
     294            0 :       GNUNET_snprintf (url,
     295              :                        sizeof (url),
     296              :                        "history/outgoing?limit=%lld&offset=%llu&timeout_ms=%llu",
     297              :                        (long long) num_results,
     298              :                        (unsigned long long) start_row,
     299              :                        tms);
     300              :     else
     301            0 :       GNUNET_snprintf (url,
     302              :                        sizeof (url),
     303              :                        "history/outgoing?limit=%lld&offset=%llu",
     304              :                        (long long) num_results,
     305              :                        (unsigned long long) start_row);
     306              :   }
     307           11 :   hh = GNUNET_new (struct TALER_BANK_DebitHistoryHandle);
     308           11 :   hh->hcb = hres_cb;
     309           11 :   hh->hcb_cls = hres_cb_cls;
     310           11 :   hh->request_url = TALER_url_join (auth->wire_gateway_url,
     311              :                                     url,
     312              :                                     NULL);
     313           11 :   if (NULL == hh->request_url)
     314              :   {
     315            0 :     GNUNET_free (hh);
     316            0 :     GNUNET_break (0);
     317            0 :     return NULL;
     318              :   }
     319           11 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     320              :               "Requesting debit history at `%s'\n",
     321              :               hh->request_url);
     322           11 :   eh = curl_easy_init ();
     323           22 :   if ( (NULL == eh) ||
     324              :        (GNUNET_OK !=
     325           11 :         TALER_BANK_setup_auth_ (eh,
     326           11 :                                 auth)) ||
     327              :        (CURLE_OK !=
     328           11 :         curl_easy_setopt (eh,
     329              :                           CURLOPT_URL,
     330              :                           hh->request_url)) )
     331              :   {
     332            0 :     GNUNET_break (0);
     333            0 :     TALER_BANK_debit_history_cancel (hh);
     334            0 :     if (NULL != eh)
     335            0 :       curl_easy_cleanup (eh);
     336            0 :     return NULL;
     337              :   }
     338           11 :   if (0 != tms)
     339              :   {
     340            0 :     GNUNET_break (CURLE_OK ==
     341              :                   curl_easy_setopt (eh,
     342              :                                     CURLOPT_TIMEOUT_MS,
     343              :                                     (long) tms + GRACE_PERIOD_MS));
     344              :   }
     345           11 :   hh->job = GNUNET_CURL_job_add2 (ctx,
     346              :                                   eh,
     347              :                                   NULL,
     348              :                                   &handle_debit_history_finished,
     349              :                                   hh);
     350           11 :   if (NULL == hh->job)
     351              :   {
     352            0 :     GNUNET_break (0);
     353              :     /* GNUNET_CURL_job_add2() already released @a eh */
     354            0 :     TALER_BANK_debit_history_cancel (hh);
     355            0 :     return NULL;
     356              :   }
     357           11 :   return hh;
     358              : }
     359              : 
     360              : 
     361              : void
     362           11 : TALER_BANK_debit_history_cancel (struct TALER_BANK_DebitHistoryHandle *hh)
     363              : {
     364           11 :   if (NULL != hh->job)
     365              :   {
     366            0 :     GNUNET_CURL_job_cancel (hh->job);
     367            0 :     hh->job = NULL;
     368              :   }
     369           11 :   GNUNET_free (hh->request_url);
     370           11 :   GNUNET_free (hh);
     371           11 : }
     372              : 
     373              : 
     374              : /* end of bank_api_debit.c */
        

Generated by: LCOV version 2.0-1