anastasis

Credential backup and recovery protocol and service
Log | Files | Refs | Submodules | README | LICENSE

lae_credit.c (9615B)


      1 /*
      2   This file is part of Anastasis
      3   Copyright (C) 2017--2021 Anastasis SARL
      4 
      5   Anastasis 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   Anastasis 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 Anastasis; see the file COPYING.  If not,
     17   see <http://www.gnu.org/licenses/>
     18 */
     19 /**
     20  * @file libanastasiseufin/lae_credit.c
     21  * @brief Implementation of the /history/incoming
     22  *        requests of the libeufin's Anastasis facade
     23  * @author Christian Grothoff
     24  * @author Marcello Stanisci
     25  */
     26 #include "platform.h"
     27 #include "lae_common.h"
     28 #include <microhttpd.h> /* just for HTTP status codes */
     29 
     30 
     31 /**
     32  * @brief A /history/incoming Handle
     33  */
     34 struct ANASTASIS_EUFIN_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   ANASTASIS_EUFIN_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 parse_account_history (struct ANASTASIS_EUFIN_CreditHistoryHandle *hh,
     70                        const json_t *history)
     71 {
     72   json_t *history_array;
     73 
     74   if (NULL == (history_array = json_object_get (history,
     75                                                 "incoming_transactions")))
     76   {
     77     GNUNET_break_op (0);
     78     return GNUNET_SYSERR;
     79   }
     80   if (! json_is_array (history_array))
     81   {
     82     GNUNET_break_op (0);
     83     return GNUNET_SYSERR;
     84   }
     85   for (size_t i = 0; i<json_array_size (history_array); i++)
     86   {
     87     struct ANASTASIS_EUFIN_CreditDetails td;
     88     uint64_t row_id;
     89     struct GNUNET_JSON_Specification hist_spec[] = {
     90       TALER_JSON_spec_amount_any ("amount",
     91                                   &td.amount),
     92       GNUNET_JSON_spec_timestamp ("date",
     93                                   &td.execution_date),
     94       GNUNET_JSON_spec_uint64 ("row_id",
     95                                &row_id),
     96       GNUNET_JSON_spec_string ("subject",
     97                                &td.wire_subject),
     98       GNUNET_JSON_spec_string ("debit_account",
     99                                &td.debit_account_uri),
    100       GNUNET_JSON_spec_string ("credit_account",
    101                                &td.credit_account_uri),
    102       GNUNET_JSON_spec_end ()
    103     };
    104     json_t *transaction = json_array_get (history_array,
    105                                           i);
    106 
    107     if (GNUNET_OK !=
    108         GNUNET_JSON_parse (transaction,
    109                            hist_spec,
    110                            NULL, NULL))
    111     {
    112       GNUNET_break_op (0);
    113       return GNUNET_SYSERR;
    114     }
    115     if (GNUNET_OK !=
    116         hh->hcb (hh->hcb_cls,
    117                  MHD_HTTP_OK,
    118                  TALER_EC_NONE,
    119                  row_id,
    120                  &td))
    121     {
    122       hh->hcb = NULL;
    123       GNUNET_JSON_parse_free (hist_spec);
    124       return GNUNET_OK;
    125     }
    126     GNUNET_JSON_parse_free (hist_spec);
    127   }
    128   return GNUNET_OK;
    129 }
    130 
    131 
    132 /**
    133  * Function called when we're done processing the
    134  * HTTP /history/incoming request.
    135  *
    136  * @param cls the `struct ANASTASIS_EUFIN_CreditHistoryHandle`
    137  * @param response_code HTTP response code, 0 on error
    138  * @param response parsed JSON result, NULL on error
    139  */
    140 static void
    141 handle_credit_history_finished (void *cls,
    142                                 long response_code,
    143                                 const void *response)
    144 {
    145   struct ANASTASIS_EUFIN_CreditHistoryHandle *hh = cls;
    146   const json_t *j = response;
    147   enum TALER_ErrorCode ec;
    148 
    149   hh->job = NULL;
    150   switch (response_code)
    151   {
    152   case 0:
    153     ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    154     break;
    155   case MHD_HTTP_OK:
    156     if (GNUNET_OK !=
    157         parse_account_history (hh,
    158                                j))
    159     {
    160       GNUNET_break_op (0);
    161       response_code = 0;
    162       ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    163       break;
    164     }
    165     response_code = MHD_HTTP_NO_CONTENT; /* signal end of list */
    166     ec = TALER_EC_NONE;
    167     break;
    168   case MHD_HTTP_NO_CONTENT:
    169     ec = TALER_EC_NONE;
    170     break;
    171   case MHD_HTTP_BAD_REQUEST:
    172     /* This should never happen, either us or the bank is buggy
    173        (or API version conflict); just pass JSON reply to the application */
    174     GNUNET_break_op (0);
    175     ec = TALER_JSON_get_error_code (j);
    176     break;
    177   case MHD_HTTP_UNAUTHORIZED:
    178     /* Nothing really to verify, bank says the HTTP Authentication
    179        failed. May happen if HTTP authentication is used and the
    180        user supplied a wrong username/password combination. */
    181     ec = TALER_JSON_get_error_code (j);
    182     break;
    183   case MHD_HTTP_NOT_FOUND:
    184     /* Nothing really to verify: the bank is either unaware
    185        of the endpoint (not a bank), or of the account.
    186        We should pass the JSON (?) reply to the application */
    187     ec = TALER_JSON_get_error_code (j);
    188     break;
    189   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    190     /* Server had an internal issue; we should retry, but this API
    191        leaves this to the application */
    192     ec = TALER_JSON_get_error_code (j);
    193     break;
    194   default:
    195     /* unexpected response code */
    196     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    197                 "Unexpected response code %u\n",
    198                 (unsigned int) response_code);
    199     ec = TALER_JSON_get_error_code (j);
    200     break;
    201   }
    202   if (NULL != hh->hcb)
    203     hh->hcb (hh->hcb_cls,
    204              response_code,
    205              ec,
    206              0LLU,
    207              NULL);
    208   ANASTASIS_EUFIN_credit_history_cancel (hh);
    209 }
    210 
    211 
    212 struct ANASTASIS_EUFIN_CreditHistoryHandle *
    213 ANASTASIS_EUFIN_credit_history (
    214   struct GNUNET_CURL_Context *ctx,
    215   const struct ANASTASIS_EUFIN_AuthenticationData *auth,
    216   uint64_t start_row,
    217   int64_t num_results,
    218   struct GNUNET_TIME_Relative timeout,
    219   ANASTASIS_EUFIN_CreditHistoryCallback hres_cb,
    220   void *hres_cb_cls)
    221 {
    222   char url[128];
    223   struct ANASTASIS_EUFIN_CreditHistoryHandle *hh;
    224   CURL *eh;
    225   unsigned long long tms;
    226 
    227   if (0 == num_results)
    228   {
    229     GNUNET_break (0);
    230     return NULL;
    231   }
    232 
    233   tms = (unsigned long long) (timeout.rel_value_us
    234                               / GNUNET_TIME_UNIT_MILLISECONDS.rel_value_us);
    235   if ( ( (UINT64_MAX == start_row) &&
    236          (0 > num_results) ) ||
    237        ( (0 == start_row) &&
    238          (0 < num_results) ) )
    239   {
    240     if ( (0 < num_results) &&
    241          (! GNUNET_TIME_relative_is_zero (timeout)) )
    242       GNUNET_snprintf (url,
    243                        sizeof (url),
    244                        "history/incoming?delta=%lld&long_poll_ms=%llu",
    245                        (long long) num_results,
    246                        tms);
    247     else
    248       GNUNET_snprintf (url,
    249                        sizeof (url),
    250                        "history/incoming?delta=%lld",
    251                        (long long) num_results);
    252   }
    253   else
    254   {
    255     if ( (0 < num_results) &&
    256          (! GNUNET_TIME_relative_is_zero (timeout)) )
    257       GNUNET_snprintf (url,
    258                        sizeof (url),
    259                        "history/incoming?delta=%lld&start=%llu&long_poll_ms=%llu",
    260                        (long long) num_results,
    261                        (unsigned long long) start_row,
    262                        tms);
    263     else
    264       GNUNET_snprintf (url,
    265                        sizeof (url),
    266                        "history/incoming?delta=%lld&start=%llu",
    267                        (long long) num_results,
    268                        (unsigned long long) start_row);
    269   }
    270   hh = GNUNET_new (struct ANASTASIS_EUFIN_CreditHistoryHandle);
    271   hh->hcb = hres_cb;
    272   hh->hcb_cls = hres_cb_cls;
    273   hh->request_url = TALER_url_join (auth->wire_gateway_url,
    274                                     url,
    275                                     NULL);
    276   if (NULL == hh->request_url)
    277   {
    278     GNUNET_free (hh);
    279     GNUNET_break (0);
    280     return NULL;
    281   }
    282   GNUNET_log (GNUNET_ERROR_TYPE_INFO,
    283               "Requesting credit history at `%s'\n",
    284               hh->request_url);
    285   eh = curl_easy_init ();
    286   if ( (NULL == eh) ||
    287        (GNUNET_OK !=
    288         ANASTASIS_EUFIN_setup_auth_ (eh,
    289                                      auth)) ||
    290        (CURLE_OK !=
    291         curl_easy_setopt (eh,
    292                           CURLOPT_URL,
    293                           hh->request_url)) )
    294   {
    295     GNUNET_break (0);
    296     ANASTASIS_EUFIN_credit_history_cancel (hh);
    297     if (NULL != eh)
    298       curl_easy_cleanup (eh);
    299     return NULL;
    300   }
    301   if (0 != tms)
    302   {
    303     GNUNET_break (CURLE_OK ==
    304                   curl_easy_setopt (eh,
    305                                     CURLOPT_TIMEOUT_MS,
    306                                     (long) tms));
    307   }
    308   hh->job = GNUNET_CURL_job_add2 (ctx,
    309                                   eh,
    310                                   NULL,
    311                                   &handle_credit_history_finished,
    312                                   hh);
    313   return hh;
    314 }
    315 
    316 
    317 void
    318 ANASTASIS_EUFIN_credit_history_cancel (
    319   struct ANASTASIS_EUFIN_CreditHistoryHandle *hh)
    320 {
    321   if (NULL != hh->job)
    322   {
    323     GNUNET_CURL_job_cancel (hh->job);
    324     hh->job = NULL;
    325   }
    326   GNUNET_free (hh->request_url);
    327   GNUNET_free (hh);
    328 }
    329 
    330 
    331 /* end of lae_credit.c */