LCOV - code coverage report
Current view: top level - lib - exchange_api_purse_create_with_merge.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 109 209 52.2 %
Date: 2025-06-05 21:03:14 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :    This file is part of TALER
       3             :    Copyright (C) 2022-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_purse_create_with_merge.c
      19             :  * @brief Implementation of the client to create a
      20             :  *        purse for an account
      21             :  * @author Christian Grothoff
      22             :  */
      23             : #include "platform.h"
      24             : #include <jansson.h>
      25             : #include <microhttpd.h> /* just for HTTP status codes */
      26             : #include <gnunet/gnunet_util_lib.h>
      27             : #include <gnunet/gnunet_json_lib.h>
      28             : #include <gnunet/gnunet_curl_lib.h>
      29             : #include "taler_json_lib.h"
      30             : #include "taler_exchange_service.h"
      31             : #include "exchange_api_handle.h"
      32             : #include "exchange_api_common.h"
      33             : #include "taler_signatures.h"
      34             : #include "exchange_api_curl_defaults.h"
      35             : 
      36             : 
      37             : /**
      38             :  * @brief A purse create with merge handle
      39             :  */
      40             : struct TALER_EXCHANGE_PurseCreateMergeHandle
      41             : {
      42             : 
      43             :   /**
      44             :    * The keys of the exchange this request handle will use
      45             :    */
      46             :   struct TALER_EXCHANGE_Keys *keys;
      47             : 
      48             :   /**
      49             :    * The url for this request.
      50             :    */
      51             :   char *url;
      52             : 
      53             :   /**
      54             :    * The exchange base URL.
      55             :    */
      56             :   char *exchange_url;
      57             : 
      58             :   /**
      59             :    * Context for #TEH_curl_easy_post(). Keeps the data that must
      60             :    * persist for Curl to make the upload.
      61             :    */
      62             :   struct TALER_CURL_PostContext ctx;
      63             : 
      64             :   /**
      65             :    * Handle for the request.
      66             :    */
      67             :   struct GNUNET_CURL_Job *job;
      68             : 
      69             :   /**
      70             :    * Function to call with the result.
      71             :    */
      72             :   TALER_EXCHANGE_PurseCreateMergeCallback cb;
      73             : 
      74             :   /**
      75             :    * Closure for @a cb.
      76             :    */
      77             :   void *cb_cls;
      78             : 
      79             :   /**
      80             :    * The encrypted contract (if any).
      81             :    */
      82             :   struct TALER_EncryptedContract econtract;
      83             : 
      84             :   /**
      85             :    * Expected value in the purse after fees.
      86             :    */
      87             :   struct TALER_Amount purse_value_after_fees;
      88             : 
      89             :   /**
      90             :    * Public key of the reserve public key.
      91             :    */
      92             :   struct TALER_ReservePublicKeyP reserve_pub;
      93             : 
      94             :   /**
      95             :    * Reserve signature affirming our merge.
      96             :    */
      97             :   struct TALER_ReserveSignatureP reserve_sig;
      98             : 
      99             :   /**
     100             :    * Merge capability key.
     101             :    */
     102             :   struct TALER_PurseMergePublicKeyP merge_pub;
     103             : 
     104             :   /**
     105             :    * Our merge signature (if any).
     106             :    */
     107             :   struct TALER_PurseMergeSignatureP merge_sig;
     108             : 
     109             :   /**
     110             :    * Public key of the purse.
     111             :    */
     112             :   struct TALER_PurseContractPublicKeyP purse_pub;
     113             : 
     114             :   /**
     115             :    * Request data we signed over.
     116             :    */
     117             :   struct TALER_PurseContractSignatureP purse_sig;
     118             : 
     119             :   /**
     120             :    * Hash over the purse's contrac terms.
     121             :    */
     122             :   struct TALER_PrivateContractHashP h_contract_terms;
     123             : 
     124             :   /**
     125             :    * When does the purse expire.
     126             :    */
     127             :   struct GNUNET_TIME_Timestamp purse_expiration;
     128             : 
     129             :   /**
     130             :    * When does the purse get merged/created.
     131             :    */
     132             :   struct GNUNET_TIME_Timestamp merge_timestamp;
     133             : };
     134             : 
     135             : 
     136             : /**
     137             :  * Function called when we're done processing the
     138             :  * HTTP /reserves/$RID/purse request.
     139             :  *
     140             :  * @param cls the `struct TALER_EXCHANGE_PurseCreateMergeHandle`
     141             :  * @param response_code HTTP response code, 0 on error
     142             :  * @param response parsed JSON result, NULL on error
     143             :  */
     144             : static void
     145          14 : handle_purse_create_with_merge_finished (void *cls,
     146             :                                          long response_code,
     147             :                                          const void *response)
     148             : {
     149          14 :   struct TALER_EXCHANGE_PurseCreateMergeHandle *pcm = cls;
     150          14 :   const json_t *j = response;
     151          14 :   struct TALER_EXCHANGE_PurseCreateMergeResponse dr = {
     152             :     .hr.reply = j,
     153          14 :     .hr.http_status = (unsigned int) response_code,
     154          14 :     .reserve_sig = &pcm->reserve_sig
     155             :   };
     156             : 
     157          14 :   pcm->job = NULL;
     158          14 :   switch (response_code)
     159             :   {
     160           0 :   case 0:
     161           0 :     dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE;
     162           0 :     break;
     163          11 :   case MHD_HTTP_OK:
     164             :     {
     165             :       struct GNUNET_TIME_Timestamp etime;
     166             :       struct TALER_Amount total_deposited;
     167             :       struct TALER_ExchangeSignatureP exchange_sig;
     168             :       struct TALER_ExchangePublicKeyP exchange_pub;
     169             :       struct GNUNET_JSON_Specification spec[] = {
     170          11 :         TALER_JSON_spec_amount_any ("total_deposited",
     171             :                                     &total_deposited),
     172          11 :         GNUNET_JSON_spec_fixed_auto ("exchange_sig",
     173             :                                      &exchange_sig),
     174          11 :         GNUNET_JSON_spec_fixed_auto ("exchange_pub",
     175             :                                      &exchange_pub),
     176          11 :         GNUNET_JSON_spec_timestamp ("exchange_timestamp",
     177             :                                     &etime),
     178          11 :         GNUNET_JSON_spec_end ()
     179             :       };
     180             : 
     181          11 :       if (GNUNET_OK !=
     182          11 :           GNUNET_JSON_parse (j,
     183             :                              spec,
     184             :                              NULL, NULL))
     185             :       {
     186           0 :         GNUNET_break_op (0);
     187           0 :         dr.hr.http_status = 0;
     188           0 :         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     189           0 :         break;
     190             :       }
     191          11 :       if (GNUNET_OK !=
     192          11 :           TALER_EXCHANGE_test_signing_key (pcm->keys,
     193             :                                            &exchange_pub))
     194             :       {
     195           0 :         GNUNET_break_op (0);
     196           0 :         dr.hr.http_status = 0;
     197           0 :         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     198           0 :         break;
     199             :       }
     200          11 :       if (GNUNET_OK !=
     201          11 :           TALER_exchange_online_purse_created_verify (
     202             :             etime,
     203             :             pcm->purse_expiration,
     204          11 :             &pcm->purse_value_after_fees,
     205             :             &total_deposited,
     206          11 :             &pcm->purse_pub,
     207          11 :             &pcm->h_contract_terms,
     208             :             &exchange_pub,
     209             :             &exchange_sig))
     210             :       {
     211           0 :         GNUNET_break_op (0);
     212           0 :         dr.hr.http_status = 0;
     213           0 :         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     214           0 :         break;
     215             :       }
     216             :     }
     217          11 :     break;
     218           0 :   case MHD_HTTP_BAD_REQUEST:
     219             :     /* This should never happen, either us or the exchange is buggy
     220             :        (or API version conflict); just pass JSON reply to the application */
     221           0 :     dr.hr.ec = TALER_JSON_get_error_code (j);
     222           0 :     dr.hr.hint = TALER_JSON_get_error_hint (j);
     223           0 :     break;
     224           0 :   case MHD_HTTP_FORBIDDEN:
     225           0 :     dr.hr.ec = TALER_JSON_get_error_code (j);
     226           0 :     dr.hr.hint = TALER_JSON_get_error_hint (j);
     227             :     /* Nothing really to verify, exchange says one of the signatures is
     228             :        invalid; as we checked them, this should never happen, we
     229             :        should pass the JSON reply to the application */
     230           0 :     break;
     231           0 :   case MHD_HTTP_NOT_FOUND:
     232           0 :     dr.hr.ec = TALER_JSON_get_error_code (j);
     233           0 :     dr.hr.hint = TALER_JSON_get_error_hint (j);
     234             :     /* Nothing really to verify, this should never
     235             :        happen, we should pass the JSON reply to the application */
     236           0 :     break;
     237           2 :   case MHD_HTTP_CONFLICT:
     238           2 :     dr.hr.ec = TALER_JSON_get_error_code (j);
     239           2 :     switch (dr.hr.ec)
     240             :     {
     241           0 :     case TALER_EC_EXCHANGE_RESERVES_PURSE_CREATE_CONFLICTING_META_DATA:
     242           0 :       if (GNUNET_OK !=
     243           0 :           TALER_EXCHANGE_check_purse_create_conflict_ (
     244           0 :             &pcm->purse_sig,
     245           0 :             &pcm->purse_pub,
     246             :             j))
     247             :       {
     248           0 :         dr.hr.http_status = 0;
     249           0 :         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     250           0 :         break;
     251             :       }
     252           0 :       break;
     253           0 :     case TALER_EC_EXCHANGE_RESERVES_PURSE_MERGE_CONFLICTING_META_DATA:
     254           0 :       if (GNUNET_OK !=
     255           0 :           TALER_EXCHANGE_check_purse_merge_conflict_ (
     256           0 :             &pcm->merge_sig,
     257           0 :             &pcm->merge_pub,
     258           0 :             &pcm->purse_pub,
     259           0 :             pcm->exchange_url,
     260             :             j))
     261             :       {
     262           0 :         GNUNET_break_op (0);
     263           0 :         dr.hr.http_status = 0;
     264           0 :         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     265           0 :         break;
     266             :       }
     267           0 :       break;
     268           2 :     case TALER_EC_EXCHANGE_RESERVES_PURSE_CREATE_INSUFFICIENT_FUNDS:
     269             :       /* nothing to verify */
     270           2 :       break;
     271           0 :     case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA:
     272           0 :       if (GNUNET_OK !=
     273           0 :           TALER_EXCHANGE_check_purse_econtract_conflict_ (
     274           0 :             &pcm->econtract.econtract_sig,
     275           0 :             &pcm->purse_pub,
     276             :             j))
     277             :       {
     278           0 :         GNUNET_break_op (0);
     279           0 :         dr.hr.http_status = 0;
     280           0 :         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     281           0 :         break;
     282             :       }
     283           0 :       break;
     284           0 :     default:
     285             :       /* unexpected EC! */
     286           0 :       GNUNET_break_op (0);
     287           0 :       dr.hr.http_status = 0;
     288           0 :       dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     289           0 :       break;
     290             :     } /* end inner (EC) switch */
     291           2 :     break;
     292           0 :   case MHD_HTTP_GONE:
     293             :     /* could happen if denomination was revoked */
     294             :     /* Note: one might want to check /keys for revocation
     295             :        signature here, alas tricky in case our /keys
     296             :        is outdated => left to clients */
     297           0 :     dr.hr.ec = TALER_JSON_get_error_code (j);
     298           0 :     dr.hr.hint = TALER_JSON_get_error_hint (j);
     299           0 :     break;
     300           1 :   case MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS:
     301             :     {
     302             :       struct GNUNET_JSON_Specification spec[] = {
     303           1 :         GNUNET_JSON_spec_uint64 (
     304             :           "requirement_row",
     305             :           &dr.details.unavailable_for_legal_reasons.requirement_row),
     306           1 :         GNUNET_JSON_spec_end ()
     307             :       };
     308             : 
     309           1 :       if (GNUNET_OK !=
     310           1 :           GNUNET_JSON_parse (j,
     311             :                              spec,
     312             :                              NULL, NULL))
     313             :       {
     314           0 :         GNUNET_break_op (0);
     315           0 :         dr.hr.http_status = 0;
     316           0 :         dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED;
     317           0 :         break;
     318             :       }
     319             :     }
     320           1 :     break;
     321           0 :   case MHD_HTTP_INTERNAL_SERVER_ERROR:
     322           0 :     dr.hr.ec = TALER_JSON_get_error_code (j);
     323           0 :     dr.hr.hint = TALER_JSON_get_error_hint (j);
     324             :     /* Server had an internal issue; we should retry, but this API
     325             :        leaves this to the application */
     326           0 :     break;
     327           0 :   default:
     328             :     /* unexpected response code */
     329           0 :     dr.hr.ec = TALER_JSON_get_error_code (j);
     330           0 :     dr.hr.hint = TALER_JSON_get_error_hint (j);
     331           0 :     GNUNET_log (GNUNET_ERROR_TYPE_ERROR,
     332             :                 "Unexpected response code %u/%d for exchange deposit\n",
     333             :                 (unsigned int) response_code,
     334             :                 dr.hr.ec);
     335           0 :     GNUNET_break_op (0);
     336           0 :     break;
     337             :   }
     338          14 :   pcm->cb (pcm->cb_cls,
     339             :            &dr);
     340          14 :   TALER_EXCHANGE_purse_create_with_merge_cancel (pcm);
     341          14 : }
     342             : 
     343             : 
     344             : struct TALER_EXCHANGE_PurseCreateMergeHandle *
     345          14 : TALER_EXCHANGE_purse_create_with_merge (
     346             :   struct GNUNET_CURL_Context *ctx,
     347             :   const char *url,
     348             :   struct TALER_EXCHANGE_Keys *keys,
     349             :   const struct TALER_ReservePrivateKeyP *reserve_priv,
     350             :   const struct TALER_PurseContractPrivateKeyP *purse_priv,
     351             :   const struct TALER_PurseMergePrivateKeyP *merge_priv,
     352             :   const struct TALER_ContractDiffiePrivateP *contract_priv,
     353             :   const json_t *contract_terms,
     354             :   bool upload_contract,
     355             :   bool pay_for_purse,
     356             :   struct GNUNET_TIME_Timestamp merge_timestamp,
     357             :   TALER_EXCHANGE_PurseCreateMergeCallback cb,
     358             :   void *cb_cls)
     359             : {
     360             :   struct TALER_EXCHANGE_PurseCreateMergeHandle *pcm;
     361             :   json_t *create_with_merge_obj;
     362             :   CURL *eh;
     363             :   char arg_str[sizeof (pcm->reserve_pub) * 2 + 32];
     364          14 :   uint32_t min_age = 0;
     365             :   struct TALER_Amount purse_fee;
     366             :   enum TALER_WalletAccountMergeFlags flags;
     367             : 
     368          14 :   pcm = GNUNET_new (struct TALER_EXCHANGE_PurseCreateMergeHandle);
     369          14 :   pcm->cb = cb;
     370          14 :   pcm->cb_cls = cb_cls;
     371          14 :   if (GNUNET_OK !=
     372          14 :       TALER_JSON_contract_hash (contract_terms,
     373             :                                 &pcm->h_contract_terms))
     374             :   {
     375           0 :     GNUNET_break (0);
     376           0 :     GNUNET_free (pcm);
     377           0 :     return NULL;
     378             :   }
     379          14 :   pcm->merge_timestamp = merge_timestamp;
     380          14 :   GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv->eddsa_priv,
     381             :                                       &pcm->purse_pub.eddsa_pub);
     382          14 :   GNUNET_CRYPTO_eddsa_key_get_public (&reserve_priv->eddsa_priv,
     383             :                                       &pcm->reserve_pub.eddsa_pub);
     384          14 :   GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv,
     385             :                                       &pcm->merge_pub.eddsa_pub);
     386             : 
     387             :   {
     388             :     struct GNUNET_JSON_Specification spec[] = {
     389          14 :       TALER_JSON_spec_amount_any ("amount",
     390             :                                   &pcm->purse_value_after_fees),
     391          14 :       GNUNET_JSON_spec_mark_optional (
     392             :         GNUNET_JSON_spec_uint32 ("minimum_age",
     393             :                                  &min_age),
     394             :         NULL),
     395          14 :       GNUNET_JSON_spec_timestamp ("pay_deadline",
     396             :                                   &pcm->purse_expiration),
     397          14 :       GNUNET_JSON_spec_end ()
     398             :     };
     399             : 
     400          14 :     if (GNUNET_OK !=
     401          14 :         GNUNET_JSON_parse (contract_terms,
     402             :                            spec,
     403             :                            NULL, NULL))
     404             :     {
     405           0 :       GNUNET_break (0);
     406           0 :       GNUNET_free (pcm);
     407           0 :       return NULL;
     408             :     }
     409             :   }
     410          14 :   if (pay_for_purse)
     411             :   {
     412             :     const struct TALER_EXCHANGE_GlobalFee *gf;
     413             : 
     414           8 :     gf = TALER_EXCHANGE_get_global_fee (
     415             :       keys,
     416             :       GNUNET_TIME_timestamp_get ());
     417           8 :     purse_fee = gf->fees.purse;
     418           8 :     flags = TALER_WAMF_MODE_CREATE_WITH_PURSE_FEE;
     419             :   }
     420             :   else
     421             :   {
     422           6 :     GNUNET_assert (GNUNET_OK ==
     423             :                    TALER_amount_set_zero (pcm->purse_value_after_fees.currency,
     424             :                                           &purse_fee));
     425           6 :     flags = TALER_WAMF_MODE_CREATE_FROM_PURSE_QUOTA;
     426             :   }
     427             : 
     428             :   {
     429             :     char pub_str[sizeof (pcm->reserve_pub) * 2];
     430             :     char *end;
     431             : 
     432          14 :     end = GNUNET_STRINGS_data_to_string (
     433          14 :       &pcm->reserve_pub,
     434             :       sizeof (pcm->reserve_pub),
     435             :       pub_str,
     436             :       sizeof (pub_str));
     437          14 :     *end = '\0';
     438          14 :     GNUNET_snprintf (arg_str,
     439             :                      sizeof (arg_str),
     440             :                      "reserves/%s/purse",
     441             :                      pub_str);
     442             :   }
     443          14 :   pcm->url = TALER_url_join (url,
     444             :                              arg_str,
     445             :                              NULL);
     446          14 :   if (NULL == pcm->url)
     447             :   {
     448           0 :     GNUNET_break (0);
     449           0 :     GNUNET_free (pcm);
     450           0 :     return NULL;
     451             :   }
     452          14 :   TALER_wallet_purse_create_sign (pcm->purse_expiration,
     453          14 :                                   &pcm->h_contract_terms,
     454          14 :                                   &pcm->merge_pub,
     455             :                                   min_age,
     456          14 :                                   &pcm->purse_value_after_fees,
     457             :                                   purse_priv,
     458             :                                   &pcm->purse_sig);
     459             :   {
     460             :     struct TALER_NormalizedPayto payto_uri;
     461             : 
     462          14 :     payto_uri = TALER_reserve_make_payto (url,
     463          14 :                                           &pcm->reserve_pub);
     464          14 :     TALER_wallet_purse_merge_sign (payto_uri,
     465             :                                    merge_timestamp,
     466          14 :                                    &pcm->purse_pub,
     467             :                                    merge_priv,
     468             :                                    &pcm->merge_sig);
     469          14 :     GNUNET_free (payto_uri.normalized_payto);
     470             :   }
     471          14 :   TALER_wallet_account_merge_sign (merge_timestamp,
     472          14 :                                    &pcm->purse_pub,
     473             :                                    pcm->purse_expiration,
     474          14 :                                    &pcm->h_contract_terms,
     475          14 :                                    &pcm->purse_value_after_fees,
     476             :                                    &purse_fee,
     477             :                                    min_age,
     478             :                                    flags,
     479             :                                    reserve_priv,
     480             :                                    &pcm->reserve_sig);
     481          14 :   if (upload_contract)
     482             :   {
     483          14 :     TALER_CRYPTO_contract_encrypt_for_deposit (
     484          14 :       &pcm->purse_pub,
     485             :       contract_priv,
     486             :       contract_terms,
     487             :       &pcm->econtract.econtract,
     488             :       &pcm->econtract.econtract_size);
     489          14 :     GNUNET_CRYPTO_ecdhe_key_get_public (&contract_priv->ecdhe_priv,
     490             :                                         &pcm->econtract.contract_pub.ecdhe_pub);
     491          14 :     TALER_wallet_econtract_upload_sign (
     492          14 :       pcm->econtract.econtract,
     493             :       pcm->econtract.econtract_size,
     494          14 :       &pcm->econtract.contract_pub,
     495             :       purse_priv,
     496             :       &pcm->econtract.econtract_sig);
     497             :   }
     498          14 :   create_with_merge_obj = GNUNET_JSON_PACK (
     499             :     TALER_JSON_pack_amount ("purse_value",
     500             :                             &pcm->purse_value_after_fees),
     501             :     GNUNET_JSON_pack_uint64 ("min_age",
     502             :                              min_age),
     503             :     GNUNET_JSON_pack_allow_null (
     504             :       TALER_JSON_pack_econtract ("econtract",
     505             :                                  upload_contract
     506             :                                  ? &pcm->econtract
     507             :                                  : NULL)),
     508             :     GNUNET_JSON_pack_allow_null (
     509             :       pay_for_purse
     510             :       ? TALER_JSON_pack_amount ("purse_fee",
     511             :                                 &purse_fee)
     512             :       : GNUNET_JSON_pack_string ("dummy2",
     513             :                                  NULL)),
     514             :     GNUNET_JSON_pack_data_auto ("merge_pub",
     515             :                                 &pcm->merge_pub),
     516             :     GNUNET_JSON_pack_data_auto ("merge_sig",
     517             :                                 &pcm->merge_sig),
     518             :     GNUNET_JSON_pack_data_auto ("reserve_sig",
     519             :                                 &pcm->reserve_sig),
     520             :     GNUNET_JSON_pack_data_auto ("purse_pub",
     521             :                                 &pcm->purse_pub),
     522             :     GNUNET_JSON_pack_data_auto ("purse_sig",
     523             :                                 &pcm->purse_sig),
     524             :     GNUNET_JSON_pack_data_auto ("h_contract_terms",
     525             :                                 &pcm->h_contract_terms),
     526             :     GNUNET_JSON_pack_timestamp ("merge_timestamp",
     527             :                                 merge_timestamp),
     528             :     GNUNET_JSON_pack_timestamp ("purse_expiration",
     529             :                                 pcm->purse_expiration));
     530          14 :   GNUNET_assert (NULL != create_with_merge_obj);
     531          14 :   eh = TALER_EXCHANGE_curl_easy_get_ (pcm->url);
     532          28 :   if ( (NULL == eh) ||
     533             :        (GNUNET_OK !=
     534          14 :         TALER_curl_easy_post (&pcm->ctx,
     535             :                               eh,
     536             :                               create_with_merge_obj)) )
     537             :   {
     538           0 :     GNUNET_break (0);
     539           0 :     if (NULL != eh)
     540           0 :       curl_easy_cleanup (eh);
     541           0 :     json_decref (create_with_merge_obj);
     542           0 :     GNUNET_free (pcm->econtract.econtract);
     543           0 :     GNUNET_free (pcm->url);
     544           0 :     GNUNET_free (pcm);
     545           0 :     return NULL;
     546             :   }
     547          14 :   json_decref (create_with_merge_obj);
     548          14 :   GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
     549             :               "URL for purse create_with_merge: `%s'\n",
     550             :               pcm->url);
     551          14 :   pcm->keys = TALER_EXCHANGE_keys_incref (keys);
     552          14 :   pcm->exchange_url = GNUNET_strdup (url);
     553          28 :   pcm->job = GNUNET_CURL_job_add2 (ctx,
     554             :                                    eh,
     555          14 :                                    pcm->ctx.headers,
     556             :                                    &handle_purse_create_with_merge_finished,
     557             :                                    pcm);
     558          14 :   return pcm;
     559             : }
     560             : 
     561             : 
     562             : void
     563          14 : TALER_EXCHANGE_purse_create_with_merge_cancel (
     564             :   struct TALER_EXCHANGE_PurseCreateMergeHandle *pcm)
     565             : {
     566          14 :   if (NULL != pcm->job)
     567             :   {
     568           0 :     GNUNET_CURL_job_cancel (pcm->job);
     569           0 :     pcm->job = NULL;
     570             :   }
     571          14 :   GNUNET_free (pcm->url);
     572          14 :   GNUNET_free (pcm->exchange_url);
     573          14 :   TALER_curl_easy_post_finished (&pcm->ctx);
     574          14 :   TALER_EXCHANGE_keys_decref (pcm->keys);
     575          14 :   GNUNET_free (pcm->econtract.econtract);
     576          14 :   GNUNET_free (pcm);
     577          14 : }
     578             : 
     579             : 
     580             : /* end of exchange_api_purse_create_with_merge.c */

Generated by: LCOV version 1.16