exchange

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

exchange_api_transfers_get.c (12402B)


      1 /*
      2   This file is part of TALER
      3   Copyright (C) 2014-2023 Taler Systems SA
      4 
      5   TALER is free software; you can redistribute it and/or modify it under the
      6   terms of the GNU General Public License as published by the Free Software
      7   Foundation; either version 3, or (at your option) any later version.
      8 
      9   TALER is distributed in the hope that it will be useful, but WITHOUT ANY
     10   WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11   A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12 
     13   You should have received a copy of the GNU General Public License along with
     14   TALER; see the file COPYING.  If not, see
     15   <http://www.gnu.org/licenses/>
     16 */
     17 /**
     18  * @file lib/exchange_api_transfers_get.c
     19  * @brief Implementation of the GET /transfers/ request
     20  * @author Christian Grothoff
     21  */
     22 #include "taler/platform.h"
     23 #include <jansson.h>
     24 #include <microhttpd.h> /* just for HTTP status codes */
     25 #include <gnunet/gnunet_util_lib.h>
     26 #include <gnunet/gnunet_curl_lib.h>
     27 #include "taler/taler_exchange_service.h"
     28 #include "taler/taler_json_lib.h"
     29 #include "exchange_api_handle.h"
     30 #include "taler/taler_signatures.h"
     31 #include "exchange_api_curl_defaults.h"
     32 
     33 
     34 /**
     35  * @brief A /transfers/ GET Handle
     36  */
     37 struct TALER_EXCHANGE_TransfersGetHandle
     38 {
     39 
     40   /**
     41    * The keys of the exchange this request handle will use
     42    */
     43   struct TALER_EXCHANGE_Keys *keys;
     44 
     45   /**
     46    * The url for this request.
     47    */
     48   char *url;
     49 
     50   /**
     51    * Handle for the request.
     52    */
     53   struct GNUNET_CURL_Job *job;
     54 
     55   /**
     56    * Function to call with the result.
     57    */
     58   TALER_EXCHANGE_TransfersGetCallback cb;
     59 
     60   /**
     61    * Closure for @a cb.
     62    */
     63   void *cb_cls;
     64 
     65 };
     66 
     67 
     68 /**
     69  * We got a #MHD_HTTP_OK response for the /transfers/ request.
     70  * Check that the response is well-formed and if it is, call the
     71  * callback.  If not, return an error code.
     72  *
     73  * This code is very similar to
     74  * merchant_api_track_transfer.c::check_transfers_get_response_ok.
     75  * Any changes should likely be reflected there as well.
     76  *
     77  * @param wdh handle to the operation
     78  * @param json response we got
     79  * @return #GNUNET_OK if we are done and all is well,
     80  *         #GNUNET_SYSERR if the response was bogus
     81  */
     82 static enum GNUNET_GenericReturnValue
     83 check_transfers_get_response_ok (
     84   struct TALER_EXCHANGE_TransfersGetHandle *wdh,
     85   const json_t *json)
     86 {
     87   const json_t *details_j;
     88   struct TALER_Amount total_expected;
     89   struct TALER_MerchantPublicKeyP merchant_pub;
     90   struct TALER_EXCHANGE_TransfersGetResponse tgr = {
     91     .hr.reply = json,
     92     .hr.http_status = MHD_HTTP_OK
     93   };
     94   struct TALER_EXCHANGE_TransferData *td
     95     = &tgr.details.ok.td;
     96   struct GNUNET_JSON_Specification spec[] = {
     97     TALER_JSON_spec_amount_any ("total",
     98                                 &td->total_amount),
     99     TALER_JSON_spec_amount_any ("wire_fee",
    100                                 &td->wire_fee),
    101     GNUNET_JSON_spec_fixed_auto ("merchant_pub",
    102                                  &merchant_pub),
    103     GNUNET_JSON_spec_fixed_auto ("h_payto",
    104                                  &td->h_payto),
    105     GNUNET_JSON_spec_timestamp ("execution_time",
    106                                 &td->execution_time),
    107     GNUNET_JSON_spec_array_const ("deposits",
    108                                   &details_j),
    109     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
    110                                  &td->exchange_sig),
    111     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
    112                                  &td->exchange_pub),
    113     GNUNET_JSON_spec_end ()
    114   };
    115 
    116   if (GNUNET_OK !=
    117       GNUNET_JSON_parse (json,
    118                          spec,
    119                          NULL, NULL))
    120   {
    121     GNUNET_break_op (0);
    122     return GNUNET_SYSERR;
    123   }
    124   if (GNUNET_OK !=
    125       TALER_amount_set_zero (td->total_amount.currency,
    126                              &total_expected))
    127   {
    128     GNUNET_break_op (0);
    129     return GNUNET_SYSERR;
    130   }
    131   if (GNUNET_OK !=
    132       TALER_EXCHANGE_test_signing_key (
    133         wdh->keys,
    134         &td->exchange_pub))
    135   {
    136     GNUNET_break_op (0);
    137     return GNUNET_SYSERR;
    138   }
    139   td->details_length = json_array_size (details_j);
    140   {
    141     struct GNUNET_HashContext *hash_context;
    142     struct TALER_TrackTransferDetails *details;
    143 
    144     details = GNUNET_new_array (td->details_length,
    145                                 struct TALER_TrackTransferDetails);
    146     td->details = details;
    147     hash_context = GNUNET_CRYPTO_hash_context_start ();
    148     for (unsigned int i = 0; i<td->details_length; i++)
    149     {
    150       struct TALER_TrackTransferDetails *detail = &details[i];
    151       struct json_t *detail_j = json_array_get (details_j, i);
    152       struct GNUNET_JSON_Specification spec_detail[] = {
    153         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
    154                                      &detail->h_contract_terms),
    155         GNUNET_JSON_spec_fixed_auto ("coin_pub", &detail->coin_pub),
    156         TALER_JSON_spec_amount ("deposit_value",
    157                                 total_expected.currency,
    158                                 &detail->coin_value),
    159         TALER_JSON_spec_amount ("deposit_fee",
    160                                 total_expected.currency,
    161                                 &detail->coin_fee),
    162         GNUNET_JSON_spec_mark_optional (
    163           TALER_JSON_spec_amount ("refund_total",
    164                                   total_expected.currency,
    165                                   &detail->refund_total),
    166           NULL),
    167         GNUNET_JSON_spec_end ()
    168       };
    169 
    170       GNUNET_assert (GNUNET_OK ==
    171                      TALER_amount_set_zero (td->total_amount.currency,
    172                                             &detail->refund_total));
    173       if ( (GNUNET_OK !=
    174             GNUNET_JSON_parse (detail_j,
    175                                spec_detail,
    176                                NULL, NULL)) ||
    177            (0 >
    178             TALER_amount_add (&total_expected,
    179                               &total_expected,
    180                               &detail->coin_value)) ||
    181            (0 >
    182             TALER_amount_subtract (&total_expected,
    183                                    &total_expected,
    184                                    &detail->coin_fee)) )
    185       {
    186         GNUNET_break_op (0);
    187         GNUNET_CRYPTO_hash_context_abort (hash_context);
    188         GNUNET_free (details);
    189         return GNUNET_SYSERR;
    190       }
    191       /* build up big hash for signature checking later */
    192       TALER_exchange_online_wire_deposit_append (
    193         hash_context,
    194         &detail->h_contract_terms,
    195         td->execution_time,
    196         &detail->coin_pub,
    197         &detail->coin_value,
    198         &detail->coin_fee);
    199     }
    200     /* Check signature */
    201     GNUNET_CRYPTO_hash_context_finish (hash_context,
    202                                        &td->h_details);
    203     if (GNUNET_OK !=
    204         TALER_exchange_online_wire_deposit_verify (
    205           &td->total_amount,
    206           &td->wire_fee,
    207           &merchant_pub,
    208           &td->h_payto,
    209           &td->h_details,
    210           &td->exchange_pub,
    211           &td->exchange_sig))
    212     {
    213       GNUNET_break_op (0);
    214       GNUNET_free (details);
    215       return GNUNET_SYSERR;
    216     }
    217     if (0 >
    218         TALER_amount_subtract (&total_expected,
    219                                &total_expected,
    220                                &td->wire_fee))
    221     {
    222       GNUNET_break_op (0);
    223       GNUNET_free (details);
    224       return GNUNET_SYSERR;
    225     }
    226     if (0 !=
    227         TALER_amount_cmp (&total_expected,
    228                           &td->total_amount))
    229     {
    230       GNUNET_break_op (0);
    231       GNUNET_free (details);
    232       return GNUNET_SYSERR;
    233     }
    234     wdh->cb (wdh->cb_cls,
    235              &tgr);
    236     GNUNET_free (details);
    237   }
    238   return GNUNET_OK;
    239 }
    240 
    241 
    242 /**
    243  * Function called when we're done processing the
    244  * HTTP /transfers/ request.
    245  *
    246  * @param cls the `struct TALER_EXCHANGE_TransfersGetHandle`
    247  * @param response_code HTTP response code, 0 on error
    248  * @param response parsed JSON result, NULL on error
    249  */
    250 static void
    251 handle_transfers_get_finished (void *cls,
    252                                long response_code,
    253                                const void *response)
    254 {
    255   struct TALER_EXCHANGE_TransfersGetHandle *wdh = cls;
    256   const json_t *j = response;
    257   struct TALER_EXCHANGE_TransfersGetResponse tgr = {
    258     .hr.reply = j,
    259     .hr.http_status = (unsigned int) response_code
    260   };
    261 
    262   wdh->job = NULL;
    263   switch (response_code)
    264   {
    265   case 0:
    266     tgr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
    267     break;
    268   case MHD_HTTP_OK:
    269     if (GNUNET_OK ==
    270         check_transfers_get_response_ok (wdh,
    271                                          j))
    272     {
    273       TALER_EXCHANGE_transfers_get_cancel (wdh);
    274       return;
    275     }
    276     GNUNET_break_op (0);
    277     tgr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
    278     tgr.hr.http_status = 0;
    279     break;
    280   case MHD_HTTP_BAD_REQUEST:
    281     /* This should never happen, either us or the exchange is buggy
    282        (or API version conflict); just pass JSON reply to the application */
    283     tgr.hr.ec = TALER_JSON_get_error_code (j);
    284     tgr.hr.hint = TALER_JSON_get_error_hint (j);
    285     break;
    286   case MHD_HTTP_FORBIDDEN:
    287     /* Nothing really to verify, exchange says one of the signatures is
    288        invalid; as we checked them, this should never happen, we
    289        should pass the JSON reply to the application */
    290     tgr.hr.ec = TALER_JSON_get_error_code (j);
    291     tgr.hr.hint = TALER_JSON_get_error_hint (j);
    292     break;
    293   case MHD_HTTP_NOT_FOUND:
    294     /* Exchange does not know about transaction;
    295        we should pass the reply to the application */
    296     tgr.hr.ec = TALER_JSON_get_error_code (j);
    297     tgr.hr.hint = TALER_JSON_get_error_hint (j);
    298     break;
    299   case MHD_HTTP_INTERNAL_SERVER_ERROR:
    300     /* Server had an internal issue; we should retry, but this API
    301        leaves this to the application */
    302     tgr.hr.ec = TALER_JSON_get_error_code (j);
    303     tgr.hr.hint = TALER_JSON_get_error_hint (j);
    304     break;
    305   default:
    306     /* unexpected response code */
    307     GNUNET_break_op (0);
    308     tgr.hr.ec = TALER_JSON_get_error_code (j);
    309     tgr.hr.hint = TALER_JSON_get_error_hint (j);
    310     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
    311                 "Unexpected response code %u/%d for transfers get\n",
    312                 (unsigned int) response_code,
    313                 (int) tgr.hr.ec);
    314     break;
    315   }
    316   wdh->cb (wdh->cb_cls,
    317            &tgr);
    318   TALER_EXCHANGE_transfers_get_cancel (wdh);
    319 }
    320 
    321 
    322 struct TALER_EXCHANGE_TransfersGetHandle *
    323 TALER_EXCHANGE_transfers_get (
    324   struct GNUNET_CURL_Context *ctx,
    325   const char *url,
    326   struct TALER_EXCHANGE_Keys *keys,
    327   const struct TALER_WireTransferIdentifierRawP *wtid,
    328   TALER_EXCHANGE_TransfersGetCallback cb,
    329   void *cb_cls)
    330 {
    331   struct TALER_EXCHANGE_TransfersGetHandle *wdh;
    332   CURL *eh;
    333   char arg_str[sizeof (struct TALER_WireTransferIdentifierRawP) * 2 + 32];
    334 
    335   wdh = GNUNET_new (struct TALER_EXCHANGE_TransfersGetHandle);
    336   wdh->cb = cb;
    337   wdh->cb_cls = cb_cls;
    338 
    339   {
    340     char wtid_str[sizeof (struct TALER_WireTransferIdentifierRawP) * 2];
    341     char *end;
    342 
    343     end = GNUNET_STRINGS_data_to_string (wtid,
    344                                          sizeof (struct
    345                                                  TALER_WireTransferIdentifierRawP),
    346                                          wtid_str,
    347                                          sizeof (wtid_str));
    348     *end = '\0';
    349     GNUNET_snprintf (arg_str,
    350                      sizeof (arg_str),
    351                      "transfers/%s",
    352                      wtid_str);
    353   }
    354   wdh->url = TALER_url_join (url,
    355                              arg_str,
    356                              NULL);
    357   if (NULL == wdh->url)
    358   {
    359     GNUNET_free (wdh);
    360     return NULL;
    361   }
    362   eh = TALER_EXCHANGE_curl_easy_get_ (wdh->url);
    363   if (NULL == eh)
    364   {
    365     GNUNET_break (0);
    366     GNUNET_free (wdh->url);
    367     GNUNET_free (wdh);
    368     return NULL;
    369   }
    370   wdh->keys = TALER_EXCHANGE_keys_incref (keys);
    371   wdh->job = GNUNET_CURL_job_add_with_ct_json (ctx,
    372                                                eh,
    373                                                &handle_transfers_get_finished,
    374                                                wdh);
    375   return wdh;
    376 }
    377 
    378 
    379 /**
    380  * Cancel wire deposits request.  This function cannot be used on a request
    381  * handle if a response is already served for it.
    382  *
    383  * @param wdh the wire deposits request handle
    384  */
    385 void
    386 TALER_EXCHANGE_transfers_get_cancel (
    387   struct TALER_EXCHANGE_TransfersGetHandle *wdh)
    388 {
    389   if (NULL != wdh->job)
    390   {
    391     GNUNET_CURL_job_cancel (wdh->job);
    392     wdh->job = NULL;
    393   }
    394   GNUNET_free (wdh->url);
    395   TALER_EXCHANGE_keys_decref (wdh->keys);
    396   GNUNET_free (wdh);
    397 }
    398 
    399 
    400 /* end of exchange_api_transfers_get.c */