exchange

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

bank_api_debit.c (10354B)


      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 parse_account_history (struct TALER_BANK_DebitHistoryHandle *hh,
     77                        const json_t *history)
     78 {
     79   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     GNUNET_JSON_spec_array_const ("outgoing_transactions",
     87                                   &history_array),
     88     TALER_JSON_spec_full_payto_uri ("debit_account",
     89                                     &dhr.details.ok.debit_account_uri),
     90     GNUNET_JSON_spec_end ()
     91   };
     92 
     93   if (GNUNET_OK !=
     94       GNUNET_JSON_parse (history,
     95                          spec,
     96                          NULL,
     97                          NULL))
     98   {
     99     GNUNET_break_op (0);
    100     return GNUNET_SYSERR;
    101   }
    102   {
    103     size_t len = json_array_size (history_array);
    104     struct TALER_BANK_DebitDetails *dd;
    105 
    106     GNUNET_break_op (0 != len);
    107     dd = GNUNET_new_array (GNUNET_NZL (len),
    108                            struct TALER_BANK_DebitDetails);
    109     for (unsigned int i = 0; i<len; i++)
    110     {
    111       struct TALER_BANK_DebitDetails *td = &dd[i];
    112       struct GNUNET_JSON_Specification hist_spec[] = {
    113         TALER_JSON_spec_amount_any ("amount",
    114                                     &td->amount),
    115         GNUNET_JSON_spec_timestamp ("date",
    116                                     &td->execution_date),
    117         GNUNET_JSON_spec_uint64 ("row_id",
    118                                  &td->serial_id),
    119         GNUNET_JSON_spec_fixed_auto ("wtid",
    120                                      &td->wtid),
    121         TALER_JSON_spec_full_payto_uri ("credit_account",
    122                                         &td->credit_account_uri),
    123         TALER_JSON_spec_web_url ("exchange_base_url",
    124                                  &td->exchange_base_url),
    125         GNUNET_JSON_spec_end ()
    126       };
    127       json_t *transaction = json_array_get (history_array,
    128                                             i);
    129 
    130       if (GNUNET_OK !=
    131           GNUNET_JSON_parse (transaction,
    132                              hist_spec,
    133                              NULL,
    134                              NULL))
    135       {
    136         GNUNET_break_op (0);
    137         GNUNET_free (dd);
    138         return GNUNET_SYSERR;
    139       }
    140     }
    141     dhr.details.ok.details_length = len;
    142     dhr.details.ok.details = dd;
    143     hh->hcb (hh->hcb_cls,
    144              &dhr);
    145     GNUNET_free (dd);
    146   }
    147   return GNUNET_OK;
    148 }
    149 
    150 
    151 /**
    152  * Function called when we're done processing the
    153  * HTTP /history/outgoing request.
    154  *
    155  * @param cls the `struct TALER_BANK_DebitHistoryHandle`
    156  * @param response_code HTTP response code, 0 on error
    157  * @param response parsed JSON result, NULL on error
    158  */
    159 static void
    160 handle_debit_history_finished (void *cls,
    161                                long response_code,
    162                                const void *response)
    163 {
    164   struct TALER_BANK_DebitHistoryHandle *hh = cls;
    165   struct TALER_BANK_DebitHistoryResponse dhr = {
    166     .http_status = response_code,
    167     .response = response
    168   };
    169 
    170   hh->job = NULL;
    171   switch (response_code)
    172   {
    173   case 0:
    174     dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    175     break;
    176   case MHD_HTTP_OK:
    177     if (GNUNET_OK !=
    178         parse_account_history (hh,
    179                                dhr.response))
    180     {
    181       GNUNET_break_op (0);
    182       dhr.http_status = 0;
    183       dhr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    184       break;
    185     }
    186     TALER_BANK_debit_history_cancel (hh);
    187     return;
    188   case MHD_HTTP_NO_CONTENT:
    189     break;
    190   case MHD_HTTP_BAD_REQUEST:
    191     /* This should never happen, either us or the bank is buggy
    192        (or API version conflict); just pass JSON reply to the application */
    193     GNUNET_break_op (0);
    194     dhr.ec = TALER_JSON_get_error_code (dhr.response);
    195     break;
    196   case MHD_HTTP_UNAUTHORIZED:
    197     /* Nothing really to verify, bank says the HTTP Authentication
    198        failed. May happen if HTTP authentication is used and the
    199        user supplied a wrong username/password combination. */
    200     dhr.ec = TALER_JSON_get_error_code (dhr.response);
    201     break;
    202   case MHD_HTTP_NOT_FOUND:
    203     /* Nothing really to verify: the bank is either unaware
    204        of the endpoint (not a bank), or of the account.
    205        We should pass the JSON (?) reply to the application */
    206     dhr.ec = TALER_JSON_get_error_code (dhr.response);
    207     break;
    208   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    209     /* Server had an internal issue; we should retry, but this API
    210        leaves this to the application */
    211     dhr.ec = TALER_JSON_get_error_code (dhr.response);
    212     break;
    213   default:
    214     /* unexpected response code */
    215     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    216                 "Unexpected response code %u\n",
    217                 (unsigned int) response_code);
    218     dhr.ec = TALER_JSON_get_error_code (dhr.response);
    219     break;
    220   }
    221   hh->hcb (hh->hcb_cls,
    222            &dhr);
    223   TALER_BANK_debit_history_cancel (hh);
    224 }
    225 
    226 
    227 struct TALER_BANK_DebitHistoryHandle *
    228 TALER_BANK_debit_history (struct GNUNET_CURL_Context *ctx,
    229                           const struct TALER_BANK_AuthenticationData *auth,
    230                           uint64_t start_row,
    231                           int64_t num_results,
    232                           struct GNUNET_TIME_Relative timeout,
    233                           TALER_BANK_DebitHistoryCallback hres_cb,
    234                           void *hres_cb_cls)
    235 {
    236   char url[128];
    237   struct TALER_BANK_DebitHistoryHandle *hh;
    238   CURL *eh;
    239   unsigned long long tms;
    240 
    241   if (0 == num_results)
    242   {
    243     GNUNET_break (0);
    244     return NULL;
    245   }
    246 
    247   tms = (unsigned long long) (timeout.rel_value_us
    248                               / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
    249   if ( ( (UINT64_MAX == start_row) &&
    250          (0 > num_results) ) ||
    251        ( (0 == start_row) &&
    252          (0 < num_results) ) )
    253   {
    254     if ( (0 < num_results) &&
    255          (! GNUNET_TIME_relative_is_zero (timeout)) )
    256       GNUNET_snprintf (url,
    257                        sizeof (url),
    258                        "history/outgoing?limit=%lld&long_poll_ms=%llu",
    259                        (long long) num_results,
    260                        tms);
    261     else
    262       GNUNET_snprintf (url,
    263                        sizeof (url),
    264                        "history/outgoing?limit=%lld",
    265                        (long long) num_results);
    266   }
    267   else
    268   {
    269     if ( (0 < num_results) &&
    270          (! GNUNET_TIME_relative_is_zero (timeout)) )
    271       GNUNET_snprintf (url,
    272                        sizeof (url),
    273                        "history/outgoing?limit=%lld&offset=%llu&long_poll_ms=%llu",
    274                        (long long) num_results,
    275                        (unsigned long long) start_row,
    276                        tms);
    277     else
    278       GNUNET_snprintf (url,
    279                        sizeof (url),
    280                        "history/outgoing?limit=%lld&offset=%llu",
    281                        (long long) num_results,
    282                        (unsigned long long) start_row);
    283   }
    284   hh = GNUNET_new (struct TALER_BANK_DebitHistoryHandle);
    285   hh->hcb = hres_cb;
    286   hh->hcb_cls = hres_cb_cls;
    287   hh->request_url = TALER_url_join (auth->wire_gateway_url,
    288                                     url,
    289                                     NULL);
    290   if (NULL == hh->request_url)
    291   {
    292     GNUNET_free (hh);
    293     GNUNET_break (0);
    294     return NULL;
    295   }
    296   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    297               "Requesting debit history at `%s'\n",
    298               hh->request_url);
    299   eh = curl_easy_init ();
    300   if ( (NULL == eh) ||
    301        (GNUNET_OK !=
    302         TALER_BANK_setup_auth_ (eh,
    303                                 auth)) ||
    304        (CURLE_OK !=
    305         curl_easy_setopt (eh,
    306                           CURLOPT_URL,
    307                           hh->request_url)) )
    308   {
    309     GNUNET_break (0);
    310     TALER_BANK_debit_history_cancel (hh);
    311     if (NULL != eh)
    312       curl_easy_cleanup (eh);
    313     return NULL;
    314   }
    315   if (0 != tms)
    316   {
    317     GNUNET_break (CURLE_OK ==
    318                   curl_easy_setopt (eh,
    319                                     CURLOPT_TIMEOUT_MS,
    320                                     (long) tms + GRACE_PERIOD_MS));
    321   }
    322   hh->job = GNUNET_CURL_job_add2 (ctx,
    323                                   eh,
    324                                   NULL,
    325                                   &handle_debit_history_finished,
    326                                   hh);
    327   return hh;
    328 }
    329 
    330 
    331 void
    332 TALER_BANK_debit_history_cancel (struct TALER_BANK_DebitHistoryHandle *hh)
    333 {
    334   if (NULL != hh->job)
    335   {
    336     GNUNET_CURL_job_cancel (hh->job);
    337     hh->job = NULL;
    338   }
    339   GNUNET_free (hh->request_url);
    340   GNUNET_free (hh);
    341 }
    342 
    343 
    344 /* end of bank_api_debit.c */