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: 166 297 55.9 %
Date: 2021-08-30 06:43:37 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14