LCOV - code coverage report
Current view: top level - bank - mb_credit.c (source / functions) Coverage Total Hit
Test: coverage.info Lines: 60.7 % 117 71
Test Date: 2025-10-31 14:20:14 Functions: 100.0 % 4 4

            Line data    Source code
       1              : /*
       2              :   This file is part of GNU 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/mb_credit.c
      21              :  * @brief Implementation of the /history
      22              :  *        requests of the libeufin's Taler merchant facade
      23              :  * @author Christian Grothoff
      24              :  * @author Marcello Stanisci
      25              :  */
      26              : #include "platform.h"
      27              : #include "mb_common.h"
      28              : #include <microhttpd.h> /* just for HTTP status codes */
      29              : 
      30              : 
      31              : /**
      32              :  * @brief A /history Handle
      33              :  */
      34              : struct TALER_MERCHANT_BANK_CreditHistoryHandle
      35              : {
      36              : 
      37              :   /**
      38              :    * The url for this request.
      39              :    */
      40              :   char *request_url;
      41              : 
      42              :   /**
      43              :    * Handle for the request.
      44              :    */
      45              :   struct GNUNET_CURL_Job *job;
      46              : 
      47              :   /**
      48              :    * Function to call with the result.
      49              :    */
      50              :   TALER_MERCHANT_BANK_CreditHistoryCallback hcb;
      51              : 
      52              :   /**
      53              :    * Closure for @a cb.
      54              :    */
      55              :   void *hcb_cls;
      56              : };
      57              : 
      58              : 
      59              : /**
      60              :  * Parse history given in JSON format and invoke the callback on each item.
      61              :  *
      62              :  * @param hh handle to the account history request
      63              :  * @param history JSON array with the history
      64              :  * @return #GNUNET_OK if history was valid and @a rhistory and @a balance
      65              :  *         were set,
      66              :  *         #GNUNET_SYSERR if there was a protocol violation in @a history
      67              :  */
      68              : static enum GNUNET_GenericReturnValue
      69            4 : parse_account_history (struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh,
      70              :                        const json_t *history)
      71              : {
      72              :   const json_t *history_array;
      73              :   struct TALER_FullPayto credit_account_uri;
      74              :   struct GNUNET_JSON_Specification spec[] = {
      75            4 :     GNUNET_JSON_spec_array_const ("incoming_transactions",
      76              :                                   &history_array),
      77            4 :     TALER_JSON_spec_full_payto_uri ("credit_account",
      78              :                                     &credit_account_uri),
      79            4 :     GNUNET_JSON_spec_end ()
      80              :   };
      81              : 
      82            4 :   if (GNUNET_OK !=
      83            4 :       GNUNET_JSON_parse (history,
      84              :                          spec,
      85              :                          NULL,
      86              :                          NULL))
      87              :   {
      88            0 :     GNUNET_break_op (0);
      89            0 :     return GNUNET_SYSERR;
      90              :   }
      91            8 :   for (unsigned int i = 0; i<json_array_size (history_array); i++)
      92              :   {
      93              :     struct TALER_MERCHANT_BANK_CreditDetails td;
      94              :     uint64_t row_id;
      95              :     struct GNUNET_JSON_Specification hist_spec[] = {
      96            4 :       TALER_JSON_spec_amount_any ("amount",
      97              :                                   &td.amount),
      98            4 :       GNUNET_JSON_spec_timestamp ("date",
      99              :                                   &td.execution_date),
     100            4 :       GNUNET_JSON_spec_uint64 ("row_id",
     101              :                                &row_id),
     102            4 :       GNUNET_JSON_spec_string ("subject",
     103              :                                &td.wire_subject),
     104            4 :       TALER_JSON_spec_full_payto_uri ("debit_account",
     105              :                                       &td.debit_account_uri),
     106            4 :       GNUNET_JSON_spec_end ()
     107              :     };
     108            4 :     json_t *transaction = json_array_get (history_array,
     109              :                                           i);
     110              : 
     111            4 :     if (GNUNET_OK !=
     112            4 :         GNUNET_JSON_parse (transaction,
     113              :                            hist_spec,
     114              :                            NULL, NULL))
     115              :     {
     116            0 :       GNUNET_break_op (0);
     117            0 :       return GNUNET_SYSERR;
     118              :     }
     119            4 :     td.credit_account_uri = credit_account_uri;
     120            4 :     if (GNUNET_OK !=
     121            4 :         hh->hcb (hh->hcb_cls,
     122              :                  MHD_HTTP_OK,
     123              :                  TALER_EC_NONE,
     124              :                  row_id,
     125              :                  &td))
     126              :     {
     127            0 :       hh->hcb = NULL;
     128            0 :       GNUNET_JSON_parse_free (hist_spec);
     129            0 :       return GNUNET_OK;
     130              :     }
     131            4 :     GNUNET_JSON_parse_free (hist_spec);
     132              :   }
     133            4 :   return GNUNET_OK;
     134              : }
     135              : 
     136              : 
     137              : /**
     138              :  * Function called when we're done processing the
     139              :  * HTTP "/history" request.
     140              :  *
     141              :  * @param cls the `struct TALER_MERCHANT_BANK_CreditHistoryHandle`
     142              :  * @param response_code HTTP response code, 0 on error
     143              :  * @param response parsed JSON result, NULL on error
     144              :  */
     145              : static void
     146            8 : handle_credit_history_finished (void *cls,
     147              :                                 long response_code,
     148              :                                 const void *response)
     149              : {
     150            8 :   struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh = cls;
     151            8 :   const json_t *j = response;
     152              :   enum TALER_ErrorCode ec;
     153              : 
     154            8 :   hh->job = NULL;
     155            8 :   switch (response_code)
     156              :   {
     157            0 :   case 0:
     158            0 :     ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     159            0 :     break;
     160            4 :   case MHD_HTTP_OK:
     161            4 :     if (GNUNET_OK !=
     162            4 :         parse_account_history (hh,
     163              :                                j))
     164              :     {
     165            0 :       GNUNET_break_op (0);
     166            0 :       response_code = 0;
     167            0 :       ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     168            0 :       break;
     169              :     }
     170            4 :     response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
     171            4 :     ec = TALER_EC_NONE;
     172            4 :     break;
     173            4 :   case MHD_HTTP_NO_CONTENT:
     174            4 :     ec = TALER_EC_NONE;
     175            4 :     break;
     176            0 :   case MHD_HTTP_BAD_REQUEST:
     177              :     /* This should never happen, either us or the bank is buggy
     178              :        (or API version conflict); just pass JSON reply to the application */
     179            0 :     GNUNET_break_op (0);
     180            0 :     ec = TALER_JSON_get_error_code (j);
     181            0 :     break;
     182            0 :   case MHD_HTTP_UNAUTHORIZED:
     183              :     /* Nothing really to verify, bank says the HTTP Authentication
     184              :        failed. May happen if HTTP authentication is used and the
     185              :        user supplied a wrong username/password combination. */
     186            0 :     ec = TALER_JSON_get_error_code (j);
     187            0 :     break;
     188            0 :   case MHD_HTTP_NOT_FOUND:
     189              :     /* Nothing really to verify: the bank is either unaware
     190              :        of the endpoint (not a bank), or of the account.
     191              :        We should pass the JSON (?) reply to the application */
     192            0 :     ec = TALER_JSON_get_error_code (j);
     193            0 :     break;
     194            0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     195              :     /* Server had an internal issue; we should retry, but this API
     196              :        leaves this to the application */
     197            0 :     ec = TALER_JSON_get_error_code (j);
     198            0 :     break;
     199            0 :   default:
     200              :     /* unexpected response code */
     201            0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     202              :                 "Unexpected response code %u\n",
     203              :                 (unsigned int) response_code);
     204            0 :     ec = TALER_JSON_get_error_code (j);
     205            0 :     break;
     206              :   }
     207            8 :   if (NULL != hh->hcb)
     208            8 :     hh->hcb (hh->hcb_cls,
     209              :              response_code,
     210              :              ec,
     211              :              0LLU,
     212              :              NULL);
     213            8 :   TALER_MERCHANT_BANK_credit_history_cancel (hh);
     214            8 : }
     215              : 
     216              : 
     217              : struct TALER_MERCHANT_BANK_CreditHistoryHandle *
     218            8 : TALER_MERCHANT_BANK_credit_history (
     219              :   struct GNUNET_CURL_Context *ctx,
     220              :   const struct TALER_MERCHANT_BANK_AuthenticationData *auth,
     221              :   uint64_t start_row,
     222              :   int64_t num_results,
     223              :   struct GNUNET_TIME_Relative timeout,
     224              :   TALER_MERCHANT_BANK_CreditHistoryCallback hres_cb,
     225              :   void *hres_cb_cls)
     226              : {
     227              :   char url[128];
     228              :   struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh;
     229              :   CURL *eh;
     230              :   unsigned long long tms;
     231              : 
     232            8 :   if (0 == num_results)
     233              :   {
     234            0 :     GNUNET_break (0);
     235            0 :     return NULL;
     236              :   }
     237              : 
     238           16 :   tms = (unsigned long long) (timeout.rel_value_us
     239            8 :                               / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
     240            8 :   if ( ( (UINT64_MAX == start_row) &&
     241            8 :          (0 > num_results) ) ||
     242            2 :        ( (0 == start_row) &&
     243              :          (0 < num_results) ) )
     244              :   {
     245            2 :     if ( (0 < num_results) &&
     246            2 :          (! GNUNET_TIME_relative_is_zero (timeout)) )
     247            0 :       GNUNET_snprintf (url,
     248              :                        sizeof (url),
     249              :                        "history?delta=%lld&long_poll_ms=%llu",
     250              :                        (long long) num_results,
     251              :                        tms);
     252              :     else
     253            2 :       GNUNET_snprintf (url,
     254              :                        sizeof (url),
     255              :                        "history?delta=%lld",
     256              :                        (long long) num_results);
     257              :   }
     258              :   else
     259              :   {
     260            6 :     if ( (0 < num_results) &&
     261            6 :          (! GNUNET_TIME_relative_is_zero (timeout)) )
     262            0 :       GNUNET_snprintf (url,
     263              :                        sizeof (url),
     264              :                        "history?delta=%lld&start=%llu&long_poll_ms=%llu",
     265              :                        (long long) num_results,
     266              :                        (unsigned long long) start_row,
     267              :                        tms);
     268              :     else
     269            6 :       GNUNET_snprintf (url,
     270              :                        sizeof (url),
     271              :                        "history?delta=%lld&start=%llu",
     272              :                        (long long) num_results,
     273              :                        (unsigned long long) start_row);
     274              :   }
     275            8 :   hh = GNUNET_new (struct TALER_MERCHANT_BANK_CreditHistoryHandle);
     276            8 :   hh->hcb = hres_cb;
     277            8 :   hh->hcb_cls = hres_cb_cls;
     278            8 :   hh->request_url = TALER_url_join (auth->wire_gateway_url,
     279              :                                     url,
     280              :                                     NULL);
     281            8 :   if (NULL == hh->request_url)
     282              :   {
     283            0 :     GNUNET_free (hh);
     284            0 :     GNUNET_break (0);
     285            0 :     return NULL;
     286              :   }
     287            8 :   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
     288              :               "Requesting credit history at `%s'\n",
     289              :               hh->request_url);
     290            8 :   eh = curl_easy_init ();
     291           16 :   if ( (NULL == eh) ||
     292              :        (GNUNET_OK !=
     293            8 :         TALER_MERCHANT_BANK_setup_auth_ (eh,
     294            8 :                                          auth)) ||
     295              :        (CURLE_OK !=
     296            8 :         curl_easy_setopt (eh,
     297              :                           CURLOPT_URL,
     298              :                           hh->request_url)) )
     299              :   {
     300            0 :     GNUNET_break (0);
     301            0 :     TALER_MERCHANT_BANK_credit_history_cancel (hh);
     302            0 :     if (NULL != eh)
     303            0 :       curl_easy_cleanup (eh);
     304            0 :     return NULL;
     305              :   }
     306            8 :   if (0 != tms)
     307              :   {
     308            0 :     GNUNET_break (CURLE_OK ==
     309              :                   curl_easy_setopt (eh,
     310              :                                     CURLOPT_TIMEOUT_MS,
     311              :                                     (long) (tms + 100L)));
     312              :   }
     313              : #if DEBUG
     314              :   GNUNET_break (CURLE_OK ==
     315              :                 curl_easy_setopt (eh,
     316              :                                   CURLOPT_VERBOSE,
     317              :                                   1L));
     318              : #endif
     319            8 :   hh->job = GNUNET_CURL_job_add2 (ctx,
     320              :                                   eh,
     321              :                                   NULL,
     322              :                                   &handle_credit_history_finished,
     323              :                                   hh);
     324            8 :   return hh;
     325              : }
     326              : 
     327              : 
     328              : void
     329            8 : TALER_MERCHANT_BANK_credit_history_cancel (
     330              :   struct TALER_MERCHANT_BANK_CreditHistoryHandle *hh)
     331              : {
     332            8 :   if (NULL != hh->job)
     333              :   {
     334            0 :     GNUNET_CURL_job_cancel (hh->job);
     335            0 :     hh->job = NULL;
     336              :   }
     337            8 :   GNUNET_free (hh->request_url);
     338            8 :   GNUNET_free (hh);
     339            8 : }
     340              : 
     341              : 
     342              : /* end of mb_credit.c */
        

Generated by: LCOV version 2.0-1