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