LCOV - code coverage report
Current view: top level - lib - exchange_api_refund.c (source / functions) Hit Total Coverage
Test: GNU Taler exchange coverage report Lines: 0 308 0.0 %
Date: 2022-08-25 06:15:09 Functions: 0 6 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :   This file is part of TALER
       3             :   Copyright (C) 2014-2022 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_refund.c
      19             :  * @brief Implementation of the /refund request of the exchange's HTTP API
      20             :  * @author Christian Grothoff
      21             :  */
      22             : #include "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_json_lib.h>
      27             : #include <gnunet/gnunet_curl_lib.h>
      28             : #include "taler_json_lib.h"
      29             : #include "taler_exchange_service.h"
      30             : #include "exchange_api_handle.h"
      31             : #include "taler_signatures.h"
      32             : #include "exchange_api_curl_defaults.h"
      33             : 
      34             : 
      35             : /**
      36             :  * @brief A Refund Handle
      37             :  */
      38             : struct TALER_EXCHANGE_RefundHandle
      39             : {
      40             : 
      41             :   /**
      42             :    * The connection to exchange this request handle will use
      43             :    */
      44             :   struct TALER_EXCHANGE_Handle *exchange;
      45             : 
      46             :   /**
      47             :    * The url for this request.
      48             :    */
      49             :   char *url;
      50             : 
      51             :   /**
      52             :    * Context for #TEH_curl_easy_post(). Keeps the data that must
      53             :    * persist for Curl to make the upload.
      54             :    */
      55             :   struct TALER_CURL_PostContext ctx;
      56             : 
      57             :   /**
      58             :    * Handle for the request.
      59             :    */
      60             :   struct GNUNET_CURL_Job *job;
      61             : 
      62             :   /**
      63             :    * Function to call with the result.
      64             :    */
      65             :   TALER_EXCHANGE_RefundCallback cb;
      66             : 
      67             :   /**
      68             :    * Closure for @a cb.
      69             :    */
      70             :   void *cb_cls;
      71             : 
      72             :   /**
      73             :    * Hash over the proposal data to identify the contract
      74             :    * which is being refunded.
      75             :    */
      76             :   struct TALER_PrivateContractHashP h_contract_terms;
      77             : 
      78             :   /**
      79             :    * The coin's public key.  This is the value that must have been
      80             :    * signed (blindly) by the Exchange.
      81             :    */
      82             :   struct TALER_CoinSpendPublicKeyP coin_pub;
      83             : 
      84             :   /**
      85             :    * The Merchant's public key.  Allows the merchant to later refund
      86             :    * the transaction or to inquire about the wire transfer identifier.
      87             :    */
      88             :   struct TALER_MerchantPublicKeyP merchant;
      89             : 
      90             :   /**
      91             :    * Merchant-generated transaction ID for the refund.
      92             :    */
      93             :   uint64_t rtransaction_id;
      94             : 
      95             :   /**
      96             :    * Amount to be refunded, including refund fee charged by the
      97             :    * exchange to the customer.
      98             :    */
      99             :   struct TALER_Amount refund_amount;
     100             : 
     101             : };
     102             : 
     103             : 
     104             : /**
     105             :  * Verify that the signature on the "200 OK" response
     106             :  * from the exchange is valid.
     107             :  *
     108             :  * @param[in,out] rh refund handle (refund fee added)
     109             :  * @param json json reply with the signature
     110             :  * @param[out] exchange_pub set to the exchange's public key
     111             :  * @param[out] exchange_sig set to the exchange's signature
     112             :  * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
     113             :  */
     114             : static enum GNUNET_GenericReturnValue
     115           0 : verify_refund_signature_ok (struct TALER_EXCHANGE_RefundHandle *rh,
     116             :                             const json_t *json,
     117             :                             struct TALER_ExchangePublicKeyP *exchange_pub,
     118             :                             struct TALER_ExchangeSignatureP *exchange_sig)
     119             : {
     120             :   const struct TALER_EXCHANGE_Keys *key_state;
     121             :   struct GNUNET_JSON_Specification spec[] = {
     122           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     123             :                                  exchange_sig),
     124           0 :     GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     125             :                                  exchange_pub),
     126           0 :     GNUNET_JSON_spec_end ()
     127             :   };
     128             : 
     129           0 :   if (GNUNET_OK !=
     130           0 :       GNUNET_JSON_parse (json,
     131             :                          spec,
     132             :                          NULL, NULL))
     133             :   {
     134           0 :     GNUNET_break_op (0);
     135           0 :     return GNUNET_SYSERR;
     136             :   }
     137           0 :   key_state = TALER_EXCHANGE_get_keys (rh->exchange);
     138           0 :   if (GNUNET_OK !=
     139           0 :       TALER_EXCHANGE_test_signing_key (key_state,
     140             :                                        exchange_pub))
     141             :   {
     142           0 :     GNUNET_break_op (0);
     143           0 :     return GNUNET_SYSERR;
     144             :   }
     145           0 :   if (GNUNET_OK !=
     146           0 :       TALER_exchange_online_refund_confirmation_verify (
     147           0 :         &rh->h_contract_terms,
     148           0 :         &rh->coin_pub,
     149           0 :         &rh->merchant,
     150             :         rh->rtransaction_id,
     151           0 :         &rh->refund_amount,
     152             :         exchange_pub,
     153             :         exchange_sig))
     154             :   {
     155           0 :     GNUNET_break_op (0);
     156           0 :     return GNUNET_SYSERR;
     157             :   }
     158           0 :   return GNUNET_OK;
     159             : }
     160             : 
     161             : 
     162             : /**
     163             :  * Verify that the information in the "409 Conflict" response
     164             :  * from the exchange is valid and indeed shows that the refund
     165             :  * amount requested is too high.
     166             :  *
     167             :  * @param[in,out] rh refund handle (refund fee added)
     168             :  * @param json json reply with the coin transaction history
     169             :  * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
     170             :  */
     171             : static enum GNUNET_GenericReturnValue
     172           0 : verify_conflict_history_ok (struct TALER_EXCHANGE_RefundHandle *rh,
     173             :                             const json_t *json)
     174             : {
     175             :   json_t *history;
     176             :   struct TALER_DenominationHashP h_denom_pub;
     177             :   struct GNUNET_JSON_Specification spec[] = {
     178           0 :     GNUNET_JSON_spec_json ("history",
     179             :                            &history),
     180           0 :     GNUNET_JSON_spec_fixed_auto ("h_denom_pub",
     181             :                                  &h_denom_pub),
     182           0 :     GNUNET_JSON_spec_end ()
     183             :   };
     184             :   size_t len;
     185             :   struct TALER_Amount dtotal;
     186             :   bool have_deposit;
     187             :   struct TALER_Amount rtotal;
     188             :   bool have_refund;
     189             : 
     190           0 :   if (GNUNET_OK !=
     191           0 :       GNUNET_JSON_parse (json,
     192             :                          spec,
     193             :                          NULL, NULL))
     194             :   {
     195           0 :     GNUNET_break_op (0);
     196           0 :     return GNUNET_SYSERR;
     197             :   }
     198           0 :   len = json_array_size (history);
     199           0 :   if (0 == len)
     200             :   {
     201           0 :     GNUNET_break_op (0);
     202           0 :     GNUNET_JSON_parse_free (spec);
     203           0 :     return GNUNET_SYSERR;
     204             :   }
     205           0 :   have_deposit = false;
     206           0 :   have_refund = false;
     207           0 :   for (size_t off = 0; off<len; off++)
     208             :   {
     209             :     json_t *transaction;
     210             :     struct TALER_Amount amount;
     211             :     const char *type;
     212             :     struct GNUNET_JSON_Specification spec_glob[] = {
     213           0 :       TALER_JSON_spec_amount_any ("amount",
     214             :                                   &amount),
     215           0 :       GNUNET_JSON_spec_string ("type",
     216             :                                &type),
     217           0 :       GNUNET_JSON_spec_end ()
     218             :     };
     219             : 
     220           0 :     transaction = json_array_get (history,
     221             :                                   off);
     222           0 :     if (GNUNET_OK !=
     223           0 :         GNUNET_JSON_parse (transaction,
     224             :                            spec_glob,
     225             :                            NULL, NULL))
     226             :     {
     227           0 :       GNUNET_break_op (0);
     228           0 :       GNUNET_JSON_parse_free (spec);
     229           0 :       return GNUNET_SYSERR;
     230             :     }
     231           0 :     if (0 == strcasecmp (type,
     232             :                          "DEPOSIT"))
     233             :     {
     234             :       struct TALER_Amount deposit_fee;
     235             :       struct TALER_MerchantWireHashP h_wire;
     236             :       struct TALER_PrivateContractHashP h_contract_terms;
     237             :       struct TALER_AgeCommitmentHash h_age_commitment;
     238             :       bool no_hac;
     239             :       // struct TALER_ExtensionContractHashP h_extensions; // FIXME #7270!
     240             :       struct GNUNET_TIME_Timestamp wallet_timestamp;
     241             :       struct TALER_MerchantPublicKeyP merchant_pub;
     242             :       struct GNUNET_TIME_Timestamp refund_deadline;
     243             :       struct TALER_CoinSpendSignatureP sig;
     244             :       struct GNUNET_JSON_Specification ispec[] = {
     245           0 :         GNUNET_JSON_spec_fixed_auto ("coin_sig",
     246             :                                      &sig),
     247           0 :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     248             :                                      &h_contract_terms),
     249           0 :         GNUNET_JSON_spec_fixed_auto ("h_wire",
     250             :                                      &h_wire),
     251           0 :         GNUNET_JSON_spec_mark_optional (
     252             :           GNUNET_JSON_spec_fixed_auto ("h_age_commitment",
     253             :                                        &h_age_commitment),
     254             :           &no_hac),
     255           0 :         GNUNET_JSON_spec_timestamp ("timestamp",
     256             :                                     &wallet_timestamp),
     257           0 :         GNUNET_JSON_spec_timestamp ("refund_deadline",
     258             :                                     &refund_deadline),
     259           0 :         TALER_JSON_spec_amount_any ("deposit_fee",
     260             :                                     &deposit_fee),
     261           0 :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     262             :                                      &merchant_pub),
     263           0 :         GNUNET_JSON_spec_end ()
     264             :       };
     265             : 
     266           0 :       if (GNUNET_OK !=
     267           0 :           GNUNET_JSON_parse (transaction,
     268             :                              ispec,
     269             :                              NULL, NULL))
     270             :       {
     271           0 :         GNUNET_break_op (0);
     272           0 :         GNUNET_JSON_parse_free (spec);
     273           0 :         return GNUNET_SYSERR;
     274             :       }
     275           0 :       if (GNUNET_OK !=
     276           0 :           TALER_wallet_deposit_verify (&amount,
     277             :                                        &deposit_fee,
     278             :                                        &h_wire,
     279             :                                        &h_contract_terms,
     280             :                                        no_hac
     281             :                                        ? NULL
     282             :                                        : &h_age_commitment,
     283             :                                        NULL /* FIXME #7270-OEC: h_extensions! */,
     284             :                                        &h_denom_pub,
     285             :                                        wallet_timestamp,
     286             :                                        &merchant_pub,
     287             :                                        refund_deadline,
     288           0 :                                        &rh->coin_pub,
     289             :                                        &sig))
     290             :       {
     291           0 :         GNUNET_break_op (0);
     292           0 :         GNUNET_JSON_parse_free (spec);
     293           0 :         return GNUNET_SYSERR;
     294             :       }
     295           0 :       if ( (0 != GNUNET_memcmp (&rh->h_contract_terms,
     296           0 :                                 &h_contract_terms)) ||
     297           0 :            (0 != GNUNET_memcmp (&rh->merchant,
     298             :                                 &merchant_pub)) )
     299             :       {
     300             :         /* deposit information is about a different merchant/contract */
     301           0 :         GNUNET_break_op (0);
     302           0 :         GNUNET_JSON_parse_free (spec);
     303           0 :         return GNUNET_SYSERR;
     304             :       }
     305           0 :       if (have_deposit)
     306             :       {
     307             :         /* this cannot really happen, but we conservatively support it anyway */
     308           0 :         if (GNUNET_YES !=
     309           0 :             TALER_amount_cmp_currency (&amount,
     310             :                                        &dtotal))
     311             :         {
     312           0 :           GNUNET_break_op (0);
     313           0 :           GNUNET_JSON_parse_free (spec);
     314           0 :           return GNUNET_SYSERR;
     315             :         }
     316           0 :         GNUNET_break (0 <=
     317             :                       TALER_amount_add (&dtotal,
     318             :                                         &dtotal,
     319             :                                         &amount));
     320             :       }
     321             :       else
     322             :       {
     323           0 :         dtotal = amount;
     324           0 :         have_deposit = true;
     325             :       }
     326             :     }
     327           0 :     else if (0 == strcasecmp (type,
     328             :                               "REFUND"))
     329             :     {
     330             :       struct TALER_MerchantSignatureP sig;
     331             :       struct TALER_Amount refund_fee;
     332             :       struct TALER_Amount sig_amount;
     333             :       struct TALER_PrivateContractHashP h_contract_terms;
     334             :       uint64_t rtransaction_id;
     335             :       struct TALER_MerchantPublicKeyP merchant_pub;
     336             :       struct GNUNET_JSON_Specification ispec[] = {
     337           0 :         TALER_JSON_spec_amount_any ("refund_fee",
     338             :                                     &refund_fee),
     339           0 :         GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     340             :                                      &sig),
     341           0 :         GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     342             :                                      &h_contract_terms),
     343           0 :         GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     344             :                                      &merchant_pub),
     345           0 :         GNUNET_JSON_spec_uint64 ("rtransaction_id",
     346             :                                  &rtransaction_id),
     347           0 :         GNUNET_JSON_spec_end ()
     348             :       };
     349             : 
     350           0 :       if (GNUNET_OK !=
     351           0 :           GNUNET_JSON_parse (transaction,
     352             :                              ispec,
     353             :                              NULL, NULL))
     354             :       {
     355           0 :         GNUNET_break_op (0);
     356           0 :         GNUNET_JSON_parse_free (spec);
     357           0 :         return GNUNET_SYSERR;
     358             :       }
     359           0 :       if (0 >
     360           0 :           TALER_amount_add (&sig_amount,
     361             :                             &refund_fee,
     362             :                             &amount))
     363             :       {
     364           0 :         GNUNET_break_op (0);
     365           0 :         GNUNET_JSON_parse_free (spec);
     366           0 :         return GNUNET_SYSERR;
     367             :       }
     368           0 :       if (GNUNET_OK !=
     369           0 :           TALER_merchant_refund_verify (&rh->coin_pub,
     370             :                                         &h_contract_terms,
     371             :                                         rtransaction_id,
     372             :                                         &sig_amount,
     373             :                                         &merchant_pub,
     374             :                                         &sig))
     375             :       {
     376           0 :         GNUNET_break_op (0);
     377           0 :         GNUNET_JSON_parse_free (spec);
     378           0 :         return GNUNET_SYSERR;
     379             :       }
     380           0 :       if ( (0 != GNUNET_memcmp (&rh->h_contract_terms,
     381           0 :                                 &h_contract_terms)) ||
     382           0 :            (0 != GNUNET_memcmp (&rh->merchant,
     383             :                                 &merchant_pub)) )
     384             :       {
     385             :         /* refund is about a different merchant/contract */
     386           0 :         GNUNET_break_op (0);
     387           0 :         GNUNET_JSON_parse_free (spec);
     388           0 :         return GNUNET_SYSERR;
     389             :       }
     390           0 :       if (rtransaction_id == rh->rtransaction_id)
     391             :       {
     392             :         /* Eh, this shows either a dependency failure or idempotency,
     393             :            but must not happen in a conflict reply. Fail! */
     394           0 :         GNUNET_break_op (0);
     395           0 :         GNUNET_JSON_parse_free (spec);
     396           0 :         return GNUNET_SYSERR;
     397             :       }
     398             : 
     399           0 :       if (have_refund)
     400             :       {
     401           0 :         if (GNUNET_YES !=
     402           0 :             TALER_amount_cmp_currency (&amount,
     403             :                                        &rtotal))
     404             :         {
     405           0 :           GNUNET_break_op (0);
     406           0 :           GNUNET_JSON_parse_free (spec);
     407           0 :           return GNUNET_SYSERR;
     408             :         }
     409           0 :         GNUNET_break (0 <=
     410             :                       TALER_amount_add (&rtotal,
     411             :                                         &rtotal,
     412             :                                         &amount));
     413             :       }
     414             :       else
     415             :       {
     416           0 :         rtotal = amount;
     417           0 :         have_refund = true;
     418             :       }
     419             :     }
     420             :     else
     421             :     {
     422             :       /* unexpected type, new version on server? */
     423           0 :       GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     424             :                   "Unexpected type `%s' in response for exchange refund\n",
     425             :                   type);
     426           0 :       GNUNET_break_op (0);
     427           0 :       GNUNET_JSON_parse_free (spec);
     428           0 :       return GNUNET_SYSERR;
     429             :     }
     430             :   }
     431             : 
     432           0 :   if (have_refund)
     433             :   {
     434           0 :     if (0 >
     435           0 :         TALER_amount_add (&rtotal,
     436             :                           &rtotal,
     437           0 :                           &rh->refund_amount))
     438             :     {
     439           0 :       GNUNET_break (0);
     440           0 :       GNUNET_JSON_parse_free (spec);
     441           0 :       return GNUNET_SYSERR;
     442             :     }
     443             :   }
     444             :   else
     445             :   {
     446           0 :     rtotal = rh->refund_amount;
     447           0 :     have_refund = true;
     448             :   }
     449           0 :   if (! have_deposit)
     450             :   {
     451           0 :     GNUNET_break (0);
     452           0 :     GNUNET_JSON_parse_free (spec);
     453           0 :     return GNUNET_SYSERR;
     454             :   }
     455           0 :   if (-1 != TALER_amount_cmp (&dtotal,
     456             :                               &rtotal))
     457             :   {
     458             :     /* rtotal <= dtotal is fine, no conflict! */
     459           0 :     GNUNET_break_op (0);
     460           0 :     GNUNET_JSON_parse_free (spec);
     461           0 :     return GNUNET_SYSERR;
     462             :   }
     463             :   /* dtotal < rtotal: that's a conflict! */
     464           0 :   GNUNET_JSON_parse_free (spec);
     465           0 :   return GNUNET_OK;
     466             : }
     467             : 
     468             : 
     469             : /**
     470             :  * Verify that the information on the "412 Dependency Failed" response
     471             :  * from the exchange is valid and indeed shows that there is a refund
     472             :  * transaction ID reuse going on.
     473             :  *
     474             :  * @param[in,out] rh refund handle (refund fee added)
     475             :  * @param json json reply with the signature
     476             :  * @return #GNUNET_OK if the signature is valid, #GNUNET_SYSERR if not
     477             :  */
     478             : static enum GNUNET_GenericReturnValue
     479           0 : verify_failed_dependency_ok (struct TALER_EXCHANGE_RefundHandle *rh,
     480             :                              const json_t *json)
     481             : {
     482             :   json_t *h;
     483             :   json_t *e;
     484             :   struct GNUNET_JSON_Specification spec[] = {
     485           0 :     GNUNET_JSON_spec_json ("history",
     486             :                            &h),
     487           0 :     GNUNET_JSON_spec_end ()
     488             :   };
     489             : 
     490           0 :   if (GNUNET_OK !=
     491           0 :       GNUNET_JSON_parse (json,
     492             :                          spec,
     493             :                          NULL, NULL))
     494             :   {
     495           0 :     GNUNET_break_op (0);
     496           0 :     return GNUNET_SYSERR;
     497             :   }
     498           0 :   if ( (! json_is_array (h)) ||
     499           0 :        (1 != json_array_size (h) ) )
     500             :   {
     501           0 :     GNUNET_break_op (0);
     502           0 :     GNUNET_JSON_parse_free (spec);
     503           0 :     return GNUNET_SYSERR;
     504             :   }
     505           0 :   e = json_array_get (h, 0);
     506             :   {
     507             :     struct TALER_Amount amount;
     508             :     const char *type;
     509             :     struct TALER_MerchantSignatureP sig;
     510             :     struct TALER_Amount refund_fee;
     511             :     struct TALER_PrivateContractHashP h_contract_terms;
     512             :     uint64_t rtransaction_id;
     513             :     struct TALER_MerchantPublicKeyP merchant_pub;
     514             :     struct GNUNET_JSON_Specification ispec[] = {
     515           0 :       TALER_JSON_spec_amount_any ("amount",
     516             :                                   &amount),
     517           0 :       GNUNET_JSON_spec_string ("type",
     518             :                                &type),
     519           0 :       TALER_JSON_spec_amount_any ("refund_fee",
     520             :                                   &refund_fee),
     521           0 :       GNUNET_JSON_spec_fixed_auto ("merchant_sig",
     522             :                                    &sig),
     523           0 :       GNUNET_JSON_spec_fixed_auto ("h_contract_terms",
     524             :                                    &h_contract_terms),
     525           0 :       GNUNET_JSON_spec_fixed_auto ("merchant_pub",
     526             :                                    &merchant_pub),
     527           0 :       GNUNET_JSON_spec_uint64 ("rtransaction_id",
     528             :                                &rtransaction_id),
     529           0 :       GNUNET_JSON_spec_end ()
     530             :     };
     531             : 
     532           0 :     if (GNUNET_OK !=
     533           0 :         GNUNET_JSON_parse (e,
     534             :                            ispec,
     535             :                            NULL, NULL))
     536             :     {
     537           0 :       GNUNET_break_op (0);
     538           0 :       GNUNET_JSON_parse_free (spec);
     539           0 :       return GNUNET_SYSERR;
     540             :     }
     541           0 :     if (GNUNET_OK !=
     542           0 :         TALER_merchant_refund_verify (&rh->coin_pub,
     543             :                                       &h_contract_terms,
     544             :                                       rtransaction_id,
     545             :                                       &amount,
     546             :                                       &merchant_pub,
     547             :                                       &sig))
     548             :     {
     549           0 :       GNUNET_break_op (0);
     550           0 :       GNUNET_JSON_parse_free (spec);
     551           0 :       return GNUNET_SYSERR;
     552             :     }
     553           0 :     if ( (rtransaction_id != rh->rtransaction_id) ||
     554           0 :          (0 != GNUNET_memcmp (&rh->h_contract_terms,
     555           0 :                               &h_contract_terms)) ||
     556           0 :          (0 != GNUNET_memcmp (&rh->merchant,
     557           0 :                               &merchant_pub)) ||
     558           0 :          (0 == TALER_amount_cmp (&rh->refund_amount,
     559             :                                  &amount)) )
     560             :     {
     561           0 :       GNUNET_break_op (0);
     562           0 :       GNUNET_JSON_parse_free (spec);
     563           0 :       return GNUNET_SYSERR;
     564             :     }
     565             :   }
     566           0 :   GNUNET_JSON_parse_free (spec);
     567           0 :   return GNUNET_OK;
     568             : }
     569             : 
     570             : 
     571             : /**
     572             :  * Function called when we're done processing the
     573             :  * HTTP /refund request.
     574             :  *
     575             :  * @param cls the `struct TALER_EXCHANGE_RefundHandle`
     576             :  * @param response_code HTTP response code, 0 on error
     577             :  * @param response parsed JSON result, NULL on error
     578             :  */
     579             : static void
     580           0 : handle_refund_finished (void *cls,
     581             :                         long response_code,
     582             :                         const void *response)
     583             : {
     584           0 :   struct TALER_EXCHANGE_RefundHandle *rh = cls;
     585             :   struct TALER_ExchangePublicKeyP exchange_pub;
     586             :   struct TALER_ExchangeSignatureP exchange_sig;
     587           0 :   struct TALER_ExchangePublicKeyP *ep = NULL;
     588           0 :   struct TALER_ExchangeSignatureP *es = NULL;
     589           0 :   const json_t *j = response;
     590           0 :   struct TALER_EXCHANGE_HttpResponse hr = {
     591             :     .reply = j,
     592           0 :     .http_status = (unsigned int) response_code
     593             :   };
     594             : 
     595           0 :   rh->job = NULL;
     596           0 :   switch (response_code)
     597             :   {
     598           0 :   case 0:
     599           0 :     hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     600           0 :     break;
     601           0 :   case MHD_HTTP_OK:
     602           0 :     if (GNUNET_OK !=
     603           0 :         verify_refund_signature_ok (rh,
     604             :                                     j,
     605             :                                     &exchange_pub,
     606             :                                     &exchange_sig))
     607             :     {
     608           0 :       GNUNET_break_op (0);
     609           0 :       hr.http_status = 0;
     610           0 :       hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_SIGNATURE_BY_EXCHANGE;
     611             :     }
     612             :     else
     613             :     {
     614           0 :       ep = &exchange_pub;
     615           0 :       es = &exchange_sig;
     616             :     }
     617           0 :     break;
     618           0 :   case MHD_HTTP_BAD_REQUEST:
     619             :     /* This should never happen, either us or the exchange is buggy
     620             :        (or API version conflict); also can happen if the currency
     621             :        differs (which we should obviously never support).
     622             :        Just pass JSON reply to the application */
     623           0 :     hr.ec = TALER_JSON_get_error_code (j);
     624           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     625           0 :     break;
     626           0 :   case MHD_HTTP_FORBIDDEN:
     627             :     /* Nothing really to verify, exchange says one of the signatures is
     628             :        invalid; as we checked them, this should never happen, we
     629             :        should pass the JSON reply to the application */
     630           0 :     hr.ec = TALER_JSON_get_error_code (j);
     631           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     632           0 :     break;
     633           0 :   case MHD_HTTP_NOT_FOUND:
     634             :     /* Nothing really to verify, this should never
     635             :        happen, we should pass the JSON reply to the application */
     636           0 :     hr.ec = TALER_JSON_get_error_code (j);
     637           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     638           0 :     break;
     639           0 :   case MHD_HTTP_CONFLICT:
     640             :     /* Requested total refunds exceed deposited amount */
     641           0 :     if (GNUNET_OK !=
     642           0 :         verify_conflict_history_ok (rh,
     643             :                                     j))
     644             :     {
     645           0 :       GNUNET_break (0);
     646           0 :       json_dumpf (j,
     647             :                   stderr,
     648             :                   JSON_INDENT (2));
     649           0 :       hr.http_status = 0;
     650           0 :       hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE;
     651           0 :       hr.hint = "conflict information provided by exchange is invalid";
     652           0 :       break;
     653             :     }
     654           0 :     hr.ec = TALER_JSON_get_error_code (j);
     655           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     656           0 :     break;
     657           0 :   case MHD_HTTP_GONE:
     658             :     /* Kind of normal: the money was already sent to the merchant
     659             :        (it was too late for the refund). */
     660           0 :     hr.ec = TALER_JSON_get_error_code (j);
     661           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     662           0 :     break;
     663           0 :   case MHD_HTTP_PRECONDITION_FAILED:
     664           0 :     if (GNUNET_OK !=
     665           0 :         verify_failed_dependency_ok (rh,
     666             :                                      j))
     667             :     {
     668           0 :       GNUNET_break (0);
     669           0 :       hr.http_status = 0;
     670           0 :       hr.ec = TALER_EC_EXCHANGE_REFUND_INVALID_FAILURE_PROOF_BY_EXCHANGE;
     671           0 :       hr.hint = "failed precondition proof returned by exchange is invalid";
     672           0 :       break;
     673             :     }
     674             :     /* Two different refund requests were made about the same deposit, but
     675             :        carrying identical refund transaction ids.  */
     676           0 :     hr.ec = TALER_JSON_get_error_code (j);
     677           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     678           0 :     break;
     679           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     680             :     /* Server had an internal issue; we should retry, but this API
     681             :        leaves this to the application */
     682           0 :     hr.ec = TALER_JSON_get_error_code (j);
     683           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     684           0 :     break;
     685           0 :   default:
     686             :     /* unexpected response code */
     687           0 :     GNUNET_break_op (0);
     688           0 :     hr.ec = TALER_JSON_get_error_code (j);
     689           0 :     hr.hint = TALER_JSON_get_error_hint (j);
     690           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     691             :                 "Unexpected response code %u/%d for exchange refund\n",
     692             :                 (unsigned int) response_code,
     693             :                 hr.ec);
     694           0 :     break;
     695             :   }
     696           0 :   rh->cb (rh->cb_cls,
     697             :           &hr,
     698             :           ep,
     699             :           es);
     700           0 :   TALER_EXCHANGE_refund_cancel (rh);
     701           0 : }
     702             : 
     703             : 
     704             : struct TALER_EXCHANGE_RefundHandle *
     705           0 : TALER_EXCHANGE_refund (
     706             :   struct TALER_EXCHANGE_Handle *exchange,
     707             :   const struct TALER_Amount *amount,
     708             :   const struct TALER_PrivateContractHashP *h_contract_terms,
     709             :   const struct TALER_CoinSpendPublicKeyP *coin_pub,
     710             :   uint64_t rtransaction_id,
     711             :   const struct TALER_MerchantPrivateKeyP *merchant_priv,
     712             :   TALER_EXCHANGE_RefundCallback cb,
     713             :   void *cb_cls)
     714             : {
     715             :   struct TALER_MerchantPublicKeyP merchant_pub;
     716             :   struct TALER_MerchantSignatureP merchant_sig;
     717             :   struct TALER_EXCHANGE_RefundHandle *rh;
     718             :   struct GNUNET_CURL_Context *ctx;
     719             :   json_t *refund_obj;
     720             :   CURL *eh;
     721             :   char arg_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2 + 32];
     722             : 
     723           0 :   GNUNET_assert (GNUNET_YES ==
     724             :                  TEAH_handle_is_ready (exchange));
     725           0 :   GNUNET_CRYPTO_eddsa_key_get_public (&merchant_priv->eddsa_priv,
     726             :                                       &merchant_pub.eddsa_pub);
     727           0 :   TALER_merchant_refund_sign (coin_pub,
     728             :                               h_contract_terms,
     729             :                               rtransaction_id,
     730             :                               amount,
     731             :                               merchant_priv,
     732             :                               &merchant_sig);
     733             :   {
     734             :     char pub_str[sizeof (struct TALER_CoinSpendPublicKeyP) * 2];
     735             :     char *end;
     736             : 
     737           0 :     end = GNUNET_STRINGS_data_to_string (
     738             :       coin_pub,
     739             :       sizeof (struct TALER_CoinSpendPublicKeyP),
     740             :       pub_str,
     741             :       sizeof (pub_str));
     742           0 :     *end = '\0';
     743           0 :     GNUNET_snprintf (arg_str,
     744             :                      sizeof (arg_str),
     745             :                      "/coins/%s/refund",
     746             :                      pub_str);
     747             :   }
     748           0 :   refund_obj = GNUNET_JSON_PACK (
     749             :     TALER_JSON_pack_amount ("refund_amount",
     750             :                             amount),
     751             :     GNUNET_JSON_pack_data_auto ("h_contract_terms",
     752             :                                 h_contract_terms),
     753             :     GNUNET_JSON_pack_uint64 ("rtransaction_id",
     754             :                              rtransaction_id),
     755             :     GNUNET_JSON_pack_data_auto ("merchant_pub",
     756             :                                 &merchant_pub),
     757             :     GNUNET_JSON_pack_data_auto ("merchant_sig",
     758             :                                 &merchant_sig));
     759           0 :   rh = GNUNET_new (struct TALER_EXCHANGE_RefundHandle);
     760           0 :   rh->exchange = exchange;
     761           0 :   rh->cb = cb;
     762           0 :   rh->cb_cls = cb_cls;
     763           0 :   rh->url = TEAH_path_to_url (exchange,
     764             :                               arg_str);
     765           0 :   if (NULL == rh->url)
     766             :   {
     767           0 :     json_decref (refund_obj);
     768           0 :     GNUNET_free (rh);
     769           0 :     return NULL;
     770             :   }
     771           0 :   rh->h_contract_terms = *h_contract_terms;
     772           0 :   rh->coin_pub = *coin_pub;
     773           0 :   rh->merchant = merchant_pub;
     774           0 :   rh->rtransaction_id = rtransaction_id;
     775           0 :   rh->refund_amount = *amount;
     776           0 :   eh = TALER_EXCHANGE_curl_easy_get_ (rh->url);
     777           0 :   if ( (NULL == eh) ||
     778             :        (GNUNET_OK !=
     779           0 :         TALER_curl_easy_post (&rh->ctx,
     780             :                               eh,
     781             :                               refund_obj)) )
     782             :   {
     783           0 :     GNUNET_break (0);
     784           0 :     if (NULL != eh)
     785           0 :       curl_easy_cleanup (eh);
     786           0 :     json_decref (refund_obj);
     787           0 :     GNUNET_free (rh->url);
     788           0 :     GNUNET_free (rh);
     789           0 :     return NULL;
     790             :   }
     791           0 :   json_decref (refund_obj);
     792           0 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     793             :               "URL for refund: `%s'\n",
     794             :               rh->url);
     795           0 :   ctx = TEAH_handle_to_context (exchange);
     796           0 :   rh->job = GNUNET_CURL_job_add2 (ctx,
     797             :                                   eh,
     798           0 :                                   rh->ctx.headers,
     799             :                                   &handle_refund_finished,
     800             :                                   rh);
     801           0 :   return rh;
     802             : }
     803             : 
     804             : 
     805             : void
     806           0 : TALER_EXCHANGE_refund_cancel (struct TALER_EXCHANGE_RefundHandle *refund)
     807             : {
     808           0 :   if (NULL != refund->job)
     809             :   {
     810           0 :     GNUNET_CURL_job_cancel (refund->job);
     811           0 :     refund->job = NULL;
     812             :   }
     813           0 :   GNUNET_free (refund->url);
     814           0 :   TALER_curl_easy_post_finished (&refund->ctx);
     815           0 :   GNUNET_free (refund);
     816           0 : }
     817             : 
     818             : 
     819             : /* end of exchange_api_refund.c */

Generated by: LCOV version 1.14