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 (10218B)


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