exchange_api_post-purses-PURSE_PUB-create.c (23018B)
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-purses-PURSE_PUB-create.c 19 * @brief Implementation of the client to create a purse with 20 * an initial set of deposits (and a contract) 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 * Information we track per deposited coin. 39 */ 40 struct Deposit 41 { 42 /** 43 * Coin's public key. 44 */ 45 struct TALER_CoinSpendPublicKeyP coin_pub; 46 47 /** 48 * Signature made with the coin. 49 */ 50 struct TALER_CoinSpendSignatureP coin_sig; 51 52 /** 53 * Coin's denomination. 54 */ 55 struct TALER_DenominationHashP h_denom_pub; 56 57 /** 58 * Signature proving the validity of the coin. 59 */ 60 struct TALER_DenominationSignature denom_sig; 61 62 /** 63 * Age restriction hash for the coin. 64 */ 65 struct TALER_AgeCommitmentHashP ahac; 66 67 /** 68 * How much did we say the coin contributed. 69 */ 70 struct TALER_Amount contribution; 71 72 /** 73 * Age attestation for this coin. Valid if @e have_age. 74 */ 75 struct TALER_AgeAttestationP attest; 76 77 /** 78 * True if this coin uses age attestation. 79 */ 80 bool have_age; 81 82 }; 83 84 85 /** 86 * @brief A purse create with deposit handle 87 */ 88 struct TALER_EXCHANGE_PostPursesCreateHandle 89 { 90 91 /** 92 * The curl context for this request. 93 */ 94 struct GNUNET_CURL_Context *ctx; 95 96 /** 97 * The base URL of the exchange. 98 */ 99 char *base_url; 100 101 /** 102 * The keys of the exchange this request handle will use 103 */ 104 struct TALER_EXCHANGE_Keys *keys; 105 106 /** 107 * The url for this request, set during _start. 108 */ 109 char *url; 110 111 /** 112 * Context for #TEH_curl_easy_post(). Keeps the data that must 113 * persist for Curl to make the upload. 114 */ 115 struct TALER_CURL_PostContext post_ctx; 116 117 /** 118 * Handle for the request. 119 */ 120 struct GNUNET_CURL_Job *job; 121 122 /** 123 * Function to call with the result. 124 */ 125 TALER_EXCHANGE_PostPursesCreateCallback cb; 126 127 /** 128 * Closure for @a cb. 129 */ 130 TALER_EXCHANGE_POST_PURSES_CREATE_RESULT_CLOSURE *cb_cls; 131 132 /** 133 * Expected value in the purse after fees. 134 */ 135 struct TALER_Amount purse_value_after_fees; 136 137 /** 138 * Our encrypted contract (if we had any). 139 */ 140 struct TALER_EncryptedContract econtract; 141 142 /** 143 * Public key of the merge capability. 144 */ 145 struct TALER_PurseMergePublicKeyP merge_pub; 146 147 /** 148 * Private key of the purse which we are creating. 149 */ 150 struct TALER_PurseContractPrivateKeyP purse_priv; 151 152 /** 153 * Private key for the merge operation. 154 */ 155 struct TALER_PurseMergePrivateKeyP merge_priv; 156 157 /** 158 * Private key to decrypt the contract. 159 */ 160 struct TALER_ContractDiffiePrivateP contract_priv; 161 162 /** 163 * Contract terms for the payment. 164 */ 165 json_t *contract_terms; 166 167 /** 168 * Minimum age, as per @e contract_terms. 169 */ 170 uint32_t min_age; 171 172 /** 173 * Public key of the purse. 174 */ 175 struct TALER_PurseContractPublicKeyP purse_pub; 176 177 /** 178 * Signature for purse creation. 179 */ 180 struct TALER_PurseContractSignatureP purse_sig; 181 182 /** 183 * Hash over the purse's contract terms. 184 */ 185 struct TALER_PrivateContractHashP h_contract_terms; 186 187 /** 188 * When does the purse expire. 189 */ 190 struct GNUNET_TIME_Timestamp purse_expiration; 191 192 /** 193 * Array of @e num_deposit deposits. 194 */ 195 struct Deposit *deposits; 196 197 /** 198 * How many deposits did we make? 199 */ 200 unsigned int num_deposits; 201 202 struct 203 { 204 205 /** 206 * Whether we are uploading a contract. 207 */ 208 bool upload_contract; 209 210 } options; 211 212 }; 213 214 215 /** 216 * Function called when we're done processing the 217 * HTTP /purses/$PID/create request. 218 * 219 * @param cls the `struct TALER_EXCHANGE_PostPursesCreateHandle` 220 * @param response_code HTTP response code, 0 on error 221 * @param response parsed JSON result, NULL on error 222 */ 223 static void 224 handle_purse_create_deposit_finished (void *cls, 225 long response_code, 226 const void *response) 227 { 228 struct TALER_EXCHANGE_PostPursesCreateHandle *pch = cls; 229 const json_t *j = response; 230 struct TALER_EXCHANGE_PostPursesCreateResponse dr = { 231 .hr.reply = j, 232 .hr.http_status = (unsigned int) response_code 233 }; 234 const struct TALER_EXCHANGE_Keys *keys = pch->keys; 235 236 pch->job = NULL; 237 switch (response_code) 238 { 239 case 0: 240 dr.hr.ec = TALER_EC_GENERIC_INVALID_RESPONSE; 241 break; 242 case MHD_HTTP_OK: 243 { 244 struct GNUNET_TIME_Timestamp etime; 245 struct TALER_Amount total_deposited; 246 struct TALER_ExchangeSignatureP exchange_sig; 247 struct TALER_ExchangePublicKeyP exchange_pub; 248 struct GNUNET_JSON_Specification spec[] = { 249 GNUNET_JSON_spec_fixed_auto ("exchange_sig", 250 &exchange_sig), 251 GNUNET_JSON_spec_fixed_auto ("exchange_pub", 252 &exchange_pub), 253 GNUNET_JSON_spec_timestamp ("exchange_timestamp", 254 &etime), 255 TALER_JSON_spec_amount ("total_deposited", 256 pch->purse_value_after_fees.currency, 257 &total_deposited), 258 GNUNET_JSON_spec_end () 259 }; 260 261 if (GNUNET_OK != 262 GNUNET_JSON_parse (j, 263 spec, 264 NULL, NULL)) 265 { 266 GNUNET_break_op (0); 267 dr.hr.http_status = 0; 268 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 269 break; 270 } 271 if (GNUNET_OK != 272 TALER_EXCHANGE_test_signing_key (keys, 273 &exchange_pub)) 274 { 275 GNUNET_break_op (0); 276 dr.hr.http_status = 0; 277 dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID; 278 break; 279 } 280 if (GNUNET_OK != 281 TALER_exchange_online_purse_created_verify ( 282 etime, 283 pch->purse_expiration, 284 &pch->purse_value_after_fees, 285 &total_deposited, 286 &pch->purse_pub, 287 &pch->h_contract_terms, 288 &exchange_pub, 289 &exchange_sig)) 290 { 291 GNUNET_break_op (0); 292 dr.hr.http_status = 0; 293 dr.hr.ec = TALER_EC_EXCHANGE_PURSE_CREATE_EXCHANGE_SIGNATURE_INVALID; 294 break; 295 } 296 dr.details.ok.exchange_pub = exchange_pub; 297 dr.details.ok.exchange_sig = exchange_sig; 298 } 299 break; 300 case MHD_HTTP_BAD_REQUEST: 301 /* This should never happen, either us or the exchange is buggy 302 (or API version conflict); just pass JSON reply to the application */ 303 dr.hr.ec = TALER_JSON_get_error_code (j); 304 dr.hr.hint = TALER_JSON_get_error_hint (j); 305 break; 306 case MHD_HTTP_FORBIDDEN: 307 dr.hr.ec = TALER_JSON_get_error_code (j); 308 dr.hr.hint = TALER_JSON_get_error_hint (j); 309 /* Nothing really to verify, exchange says one of the signatures is 310 invalid; as we checked them, this should never happen, we 311 should pass the JSON reply to the application */ 312 break; 313 case MHD_HTTP_NOT_FOUND: 314 dr.hr.ec = TALER_JSON_get_error_code (j); 315 dr.hr.hint = TALER_JSON_get_error_hint (j); 316 /* Nothing really to verify, this should never 317 happen, we should pass the JSON reply to the application */ 318 break; 319 case MHD_HTTP_CONFLICT: 320 { 321 dr.hr.ec = TALER_JSON_get_error_code (j); 322 switch (dr.hr.ec) 323 { 324 case TALER_EC_EXCHANGE_PURSE_CREATE_CONFLICTING_META_DATA: 325 if (GNUNET_OK != 326 TALER_EXCHANGE_check_purse_create_conflict_ ( 327 &pch->purse_sig, 328 &pch->purse_pub, 329 j)) 330 { 331 GNUNET_break_op (0); 332 dr.hr.http_status = 0; 333 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 334 break; 335 } 336 break; 337 case TALER_EC_EXCHANGE_GENERIC_INSUFFICIENT_FUNDS: 338 /* Nothing to check anymore here, proof needs to be 339 checked in the GET /coins/$COIN_PUB handler */ 340 break; 341 case TALER_EC_EXCHANGE_GENERIC_COIN_CONFLICTING_DENOMINATION_KEY: 342 // FIXME #7267: write check (add to exchange_api_common!) */ 343 break; 344 case TALER_EC_EXCHANGE_PURSE_DEPOSIT_CONFLICTING_META_DATA: 345 { 346 struct TALER_CoinSpendPublicKeyP coin_pub; 347 struct TALER_CoinSpendSignatureP coin_sig; 348 struct TALER_DenominationHashP h_denom_pub; 349 struct TALER_AgeCommitmentHashP phac; 350 bool found = false; 351 352 if (GNUNET_OK != 353 TALER_EXCHANGE_check_purse_coin_conflict_ ( 354 &pch->purse_pub, 355 pch->base_url, 356 j, 357 &h_denom_pub, 358 &phac, 359 &coin_pub, 360 &coin_sig)) 361 { 362 GNUNET_break_op (0); 363 dr.hr.http_status = 0; 364 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 365 break; 366 } 367 for (unsigned int i = 0; i<pch->num_deposits; i++) 368 { 369 struct Deposit *deposit = &pch->deposits[i]; 370 371 if (0 != 372 GNUNET_memcmp (&coin_pub, 373 &deposit->coin_pub)) 374 continue; 375 if (0 != 376 GNUNET_memcmp (&deposit->h_denom_pub, 377 &h_denom_pub)) 378 { 379 found = true; 380 break; 381 } 382 if (0 != 383 GNUNET_memcmp (&deposit->ahac, 384 &phac)) 385 { 386 found = true; 387 break; 388 } 389 if (0 == 390 GNUNET_memcmp (&coin_sig, 391 &deposit->coin_sig)) 392 { 393 GNUNET_break_op (0); 394 continue; 395 } 396 found = true; 397 break; 398 } 399 if (! found) 400 { 401 /* conflict is for a different coin! */ 402 GNUNET_break_op (0); 403 dr.hr.http_status = 0; 404 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 405 break; 406 } 407 } 408 /* fall-through */ 409 case TALER_EC_EXCHANGE_PURSE_ECONTRACT_CONFLICTING_META_DATA: 410 if (GNUNET_OK != 411 TALER_EXCHANGE_check_purse_econtract_conflict_ ( 412 &pch->econtract.econtract_sig, 413 &pch->purse_pub, 414 j)) 415 { 416 GNUNET_break_op (0); 417 dr.hr.http_status = 0; 418 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 419 break; 420 } 421 break; 422 default: 423 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 424 "Unexpected error code %d for conflicting deposit\n", 425 dr.hr.ec); 426 GNUNET_break_op (0); 427 dr.hr.http_status = 0; 428 dr.hr.ec = TALER_EC_GENERIC_REPLY_MALFORMED; 429 } 430 } 431 break; 432 case MHD_HTTP_GONE: 433 /* could happen if denomination was revoked */ 434 /* Note: one might want to check /keys for revocation 435 signature here, alas tricky in case our /keys 436 is outdated => left to clients */ 437 dr.hr.ec = TALER_JSON_get_error_code (j); 438 dr.hr.hint = TALER_JSON_get_error_hint (j); 439 break; 440 case MHD_HTTP_INTERNAL_SERVER_ERROR: 441 dr.hr.ec = TALER_JSON_get_error_code (j); 442 dr.hr.hint = TALER_JSON_get_error_hint (j); 443 /* Server had an internal issue; we should retry, but this API 444 leaves this to the application */ 445 break; 446 default: 447 /* unexpected response code */ 448 dr.hr.ec = TALER_JSON_get_error_code (j); 449 dr.hr.hint = TALER_JSON_get_error_hint (j); 450 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 451 "Unexpected response code %u/%d for exchange deposit\n", 452 (unsigned int) response_code, 453 dr.hr.ec); 454 GNUNET_break_op (0); 455 break; 456 } 457 if (NULL != pch->cb) 458 { 459 pch->cb (pch->cb_cls, 460 &dr); 461 pch->cb = NULL; 462 } 463 TALER_EXCHANGE_post_purses_create_cancel (pch); 464 } 465 466 467 struct TALER_EXCHANGE_PostPursesCreateHandle * 468 TALER_EXCHANGE_post_purses_create_create ( 469 struct GNUNET_CURL_Context *ctx, 470 const char *url, 471 struct TALER_EXCHANGE_Keys *keys, 472 const struct TALER_PurseContractPrivateKeyP *purse_priv, 473 const struct TALER_PurseMergePrivateKeyP *merge_priv, 474 const struct TALER_ContractDiffiePrivateP *contract_priv, 475 const json_t *contract_terms, 476 unsigned int num_deposits, 477 const struct TALER_EXCHANGE_PurseDeposit deposits[static num_deposits]) 478 { 479 struct TALER_EXCHANGE_PostPursesCreateHandle *pch; 480 481 pch = GNUNET_new (struct TALER_EXCHANGE_PostPursesCreateHandle); 482 pch->ctx = ctx; 483 pch->base_url = GNUNET_strdup (url); 484 pch->purse_priv = *purse_priv; 485 pch->merge_priv = *merge_priv; 486 pch->contract_priv = *contract_priv; 487 pch->contract_terms = json_incref ((json_t *) contract_terms); 488 { 489 struct GNUNET_JSON_Specification spec[] = { 490 GNUNET_JSON_spec_timestamp ("pay_deadline", 491 &pch->purse_expiration), 492 TALER_JSON_spec_amount_any ("amount", 493 &pch->purse_value_after_fees), 494 GNUNET_JSON_spec_mark_optional ( 495 GNUNET_JSON_spec_uint32 ("minimum_age", 496 &pch->min_age), 497 NULL), 498 GNUNET_JSON_spec_end () 499 }; 500 501 if (GNUNET_OK != 502 GNUNET_JSON_parse (contract_terms, 503 spec, 504 NULL, NULL)) 505 { 506 GNUNET_break (0); 507 GNUNET_free (pch->base_url); 508 GNUNET_free (pch); 509 return NULL; 510 } 511 } 512 if (GNUNET_OK != 513 TALER_JSON_contract_hash (contract_terms, 514 &pch->h_contract_terms)) 515 { 516 GNUNET_break (0); 517 GNUNET_free (pch->base_url); 518 GNUNET_free (pch); 519 return NULL; 520 } 521 GNUNET_CRYPTO_eddsa_key_get_public (&purse_priv->eddsa_priv, 522 &pch->purse_pub.eddsa_pub); 523 GNUNET_CRYPTO_eddsa_key_get_public (&merge_priv->eddsa_priv, 524 &pch->merge_pub.eddsa_pub); 525 pch->num_deposits = num_deposits; 526 pch->deposits = GNUNET_new_array (num_deposits, 527 struct Deposit); 528 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 529 "Signing with URL `%s'\n", 530 url); 531 for (unsigned int i = 0; i<num_deposits; i++) 532 { 533 const struct TALER_EXCHANGE_PurseDeposit *deposit = &deposits[i]; 534 const struct TALER_AgeCommitmentProof *acp = deposit->age_commitment_proof; 535 struct Deposit *d = &pch->deposits[i]; 536 537 if (NULL != acp) 538 { 539 TALER_age_commitment_hash (&acp->commitment, 540 &d->ahac); 541 d->have_age = true; 542 if (GNUNET_OK != 543 TALER_age_commitment_attest (acp, 544 pch->min_age, 545 &d->attest)) 546 { 547 GNUNET_break (0); 548 GNUNET_array_grow (pch->deposits, 549 pch->num_deposits, 550 0); 551 GNUNET_free (pch->base_url); 552 GNUNET_free (pch); 553 return NULL; 554 } 555 } 556 d->contribution = deposit->amount; 557 d->h_denom_pub = deposit->h_denom_pub; 558 TALER_denom_sig_copy (&d->denom_sig, 559 &deposit->denom_sig); 560 GNUNET_CRYPTO_eddsa_key_get_public (&deposit->coin_priv.eddsa_priv, 561 &d->coin_pub.eddsa_pub); 562 TALER_wallet_purse_deposit_sign ( 563 url, 564 &pch->purse_pub, 565 &deposit->amount, 566 &d->h_denom_pub, 567 &d->ahac, 568 &deposit->coin_priv, 569 &d->coin_sig); 570 } 571 pch->keys = TALER_EXCHANGE_keys_incref (keys); 572 return pch; 573 } 574 575 576 enum GNUNET_GenericReturnValue 577 TALER_EXCHANGE_post_purses_create_set_options_ ( 578 struct TALER_EXCHANGE_PostPursesCreateHandle *ppch, 579 unsigned int num_options, 580 const struct TALER_EXCHANGE_PostPursesCreateOptionValue options[]) 581 { 582 for (unsigned int i = 0; i < num_options; i++) 583 { 584 const struct TALER_EXCHANGE_PostPursesCreateOptionValue *opt = &options[i]; 585 586 switch (opt->option) 587 { 588 case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_END: 589 return GNUNET_OK; 590 case TALER_EXCHANGE_POST_PURSES_CREATE_OPTION_UPLOAD_CONTRACT: 591 ppch->options.upload_contract = true; 592 break; 593 } 594 } 595 return GNUNET_OK; 596 } 597 598 599 enum TALER_ErrorCode 600 TALER_EXCHANGE_post_purses_create_start ( 601 struct TALER_EXCHANGE_PostPursesCreateHandle *pch, 602 TALER_EXCHANGE_PostPursesCreateCallback cb, 603 TALER_EXCHANGE_POST_PURSES_CREATE_RESULT_CLOSURE *cb_cls) 604 { 605 char arg_str[sizeof (pch->purse_pub) * 2 + 32]; 606 CURL *eh; 607 json_t *deposit_arr; 608 json_t *body; 609 610 pch->cb = cb; 611 pch->cb_cls = cb_cls; 612 deposit_arr = json_array (); 613 GNUNET_assert (NULL != deposit_arr); 614 615 for (unsigned int i = 0; i<pch->num_deposits; i++) 616 { 617 struct Deposit *d = &pch->deposits[i]; 618 struct TALER_AgeCommitmentHashP *aghp = NULL; 619 struct TALER_AgeAttestationP *attestp = NULL; 620 json_t *jdeposit; 621 622 if (d->have_age) 623 { 624 aghp = &d->ahac; 625 attestp = &d->attest; 626 } 627 jdeposit = GNUNET_JSON_PACK ( 628 GNUNET_JSON_pack_allow_null ( 629 GNUNET_JSON_pack_data_auto ("h_age_commitment", 630 aghp)), 631 GNUNET_JSON_pack_allow_null ( 632 GNUNET_JSON_pack_data_auto ("age_attestation", 633 attestp)), 634 TALER_JSON_pack_amount ("amount", 635 &d->contribution), 636 GNUNET_JSON_pack_data_auto ("denom_pub_hash", 637 &d->h_denom_pub), 638 TALER_JSON_pack_denom_sig ("ub_sig", 639 &d->denom_sig), 640 GNUNET_JSON_pack_data_auto ("coin_sig", 641 &d->coin_sig), 642 GNUNET_JSON_pack_data_auto ("coin_pub", 643 &d->coin_pub)); 644 GNUNET_assert (0 == 645 json_array_append_new (deposit_arr, 646 jdeposit)); 647 648 } 649 650 if (pch->options.upload_contract) 651 { 652 TALER_CRYPTO_contract_encrypt_for_merge ( 653 &pch->purse_pub, 654 &pch->contract_priv, 655 &pch->merge_priv, 656 pch->contract_terms, 657 &pch->econtract.econtract, 658 &pch->econtract.econtract_size); 659 GNUNET_CRYPTO_ecdhe_key_get_public ( 660 &pch->contract_priv.ecdhe_priv, 661 &pch->econtract.contract_pub.ecdhe_pub); 662 TALER_wallet_econtract_upload_sign ( 663 pch->econtract.econtract, 664 pch->econtract.econtract_size, 665 &pch->econtract.contract_pub, 666 &pch->purse_priv, 667 &pch->econtract.econtract_sig); 668 } 669 TALER_wallet_purse_create_sign (pch->purse_expiration, 670 &pch->h_contract_terms, 671 &pch->merge_pub, 672 pch->min_age, 673 &pch->purse_value_after_fees, 674 &pch->purse_priv, 675 &pch->purse_sig); 676 677 body = GNUNET_JSON_PACK ( 678 TALER_JSON_pack_amount ("amount", 679 &pch->purse_value_after_fees), 680 GNUNET_JSON_pack_uint64 ("min_age", 681 pch->min_age), 682 GNUNET_JSON_pack_allow_null ( 683 TALER_JSON_pack_econtract ("econtract", 684 pch->options.upload_contract 685 ? &pch->econtract 686 : NULL)), 687 GNUNET_JSON_pack_data_auto ("purse_sig", 688 &pch->purse_sig), 689 GNUNET_JSON_pack_data_auto ("merge_pub", 690 &pch->merge_pub), 691 GNUNET_JSON_pack_data_auto ("h_contract_terms", 692 &pch->h_contract_terms), 693 GNUNET_JSON_pack_timestamp ("purse_expiration", 694 pch->purse_expiration), 695 GNUNET_JSON_pack_array_steal ("deposits", 696 deposit_arr)); 697 if (NULL == body) 698 { 699 GNUNET_break (0); 700 return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; 701 } 702 703 { 704 char pub_str[sizeof (pch->purse_pub) * 2]; 705 char *end; 706 707 end = GNUNET_STRINGS_data_to_string ( 708 &pch->purse_pub, 709 sizeof (pch->purse_pub), 710 pub_str, 711 sizeof (pub_str)); 712 *end = '\0'; 713 GNUNET_snprintf (arg_str, 714 sizeof (arg_str), 715 "purses/%s/create", 716 pub_str); 717 } 718 pch->url = TALER_url_join (pch->base_url, 719 arg_str, 720 NULL); 721 if (NULL == pch->url) 722 { 723 GNUNET_break (0); 724 return TALER_EC_GENERIC_CONFIGURATION_INVALID; 725 } 726 GNUNET_log (GNUNET_ERROR_TYPE_DEBUG, 727 "URL for purse create with deposit: `%s'\n", 728 pch->url); 729 eh = TALER_EXCHANGE_curl_easy_get_ (pch->url); 730 if ( (NULL == eh) || 731 (GNUNET_OK != 732 TALER_curl_easy_post (&pch->post_ctx, 733 eh, 734 body)) ) 735 { 736 GNUNET_break (0); 737 if (NULL != eh) 738 curl_easy_cleanup (eh); 739 json_decref (body); 740 return TALER_EC_GENERIC_CURL_ALLOCATION_FAILURE; 741 } 742 json_decref (body); 743 pch->job = GNUNET_CURL_job_add2 (pch->ctx, 744 eh, 745 pch->post_ctx.headers, 746 &handle_purse_create_deposit_finished, 747 pch); 748 if (NULL == pch->job) 749 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 750 return TALER_EC_NONE; 751 } 752 753 754 void 755 TALER_EXCHANGE_post_purses_create_cancel ( 756 struct TALER_EXCHANGE_PostPursesCreateHandle *pch) 757 { 758 if (NULL != pch->job) 759 { 760 GNUNET_CURL_job_cancel (pch->job); 761 pch->job = NULL; 762 } 763 json_decref (pch->contract_terms); 764 GNUNET_free (pch->econtract.econtract); 765 GNUNET_free (pch->base_url); 766 GNUNET_free (pch->url); 767 768 for (unsigned int i = 0; i<pch->num_deposits; i++) 769 { 770 struct Deposit *d = &pch->deposits[i]; 771 772 TALER_denom_sig_free (&d->denom_sig); 773 } 774 GNUNET_array_grow (pch->deposits, 775 pch->num_deposits, 776 0); 777 TALER_EXCHANGE_keys_decref (pch->keys); 778 TALER_curl_easy_post_finished (&pch->post_ctx); 779 GNUNET_free (pch); 780 } 781 782 783 /* end of exchange_api_post-purses-PURSE_PUB-create.c */