exchange_api_common.c (22776B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2015-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_common.c 19 * @brief common functions for the exchange API 20 * @author Christian Grothoff 21 */ 22 #include "taler/taler_json_lib.h" 23 #include <gnunet/gnunet_curl_lib.h> 24 #include "exchange_api_common.h" 25 #include "exchange_api_handle.h" 26 #include "taler/taler_signatures.h" 27 28 29 const struct TALER_EXCHANGE_SigningPublicKey * 30 TALER_EXCHANGE_get_signing_key_info ( 31 const struct TALER_EXCHANGE_Keys *keys, 32 const struct TALER_ExchangePublicKeyP *exchange_pub) 33 { 34 for (unsigned int i = 0; i<keys->num_sign_keys; i++) 35 { 36 const struct TALER_EXCHANGE_SigningPublicKey *spk 37 = &keys->sign_keys[i]; 38 39 if (0 == GNUNET_memcmp (exchange_pub, 40 &spk->key)) 41 return spk; 42 } 43 return NULL; 44 } 45 46 47 enum GNUNET_GenericReturnValue 48 TALER_EXCHANGE_check_purse_create_conflict_ ( 49 const struct TALER_PurseContractSignatureP *cpurse_sig, 50 const struct TALER_PurseContractPublicKeyP *purse_pub, 51 const json_t *proof) 52 { 53 struct TALER_Amount amount; 54 uint32_t min_age; 55 struct GNUNET_TIME_Timestamp purse_expiration; 56 struct TALER_PurseContractSignatureP purse_sig; 57 struct TALER_PrivateContractHashP h_contract_terms; 58 struct TALER_PurseMergePublicKeyP merge_pub; 59 struct GNUNET_JSON_Specification spec[] = { 60 TALER_JSON_spec_amount_any ("amount", 61 &amount), 62 GNUNET_JSON_spec_uint32 ("min_age", 63 &min_age), 64 GNUNET_JSON_spec_timestamp ("purse_expiration", 65 &purse_expiration), 66 GNUNET_JSON_spec_fixed_auto ("purse_sig", 67 &purse_sig), 68 GNUNET_JSON_spec_fixed_auto ("h_contract_terms", 69 &h_contract_terms), 70 GNUNET_JSON_spec_fixed_auto ("merge_pub", 71 &merge_pub), 72 GNUNET_JSON_spec_end () 73 }; 74 75 if (GNUNET_OK != 76 GNUNET_JSON_parse (proof, 77 spec, 78 NULL, NULL)) 79 { 80 GNUNET_break_op (0); 81 return GNUNET_SYSERR; 82 } 83 if (GNUNET_OK != 84 TALER_wallet_purse_create_verify (purse_expiration, 85 &h_contract_terms, 86 &merge_pub, 87 min_age, 88 &amount, 89 purse_pub, 90 &purse_sig)) 91 { 92 GNUNET_break_op (0); 93 return GNUNET_SYSERR; 94 } 95 if (0 == 96 GNUNET_memcmp (&purse_sig, 97 cpurse_sig)) 98 { 99 /* Must be the SAME data, not a conflict! */ 100 GNUNET_break_op (0); 101 return GNUNET_SYSERR; 102 } 103 return GNUNET_OK; 104 } 105 106 107 enum GNUNET_GenericReturnValue 108 TALER_EXCHANGE_check_purse_merge_conflict_ ( 109 const struct TALER_PurseMergeSignatureP *cmerge_sig, 110 const struct TALER_PurseMergePublicKeyP *merge_pub, 111 const struct TALER_PurseContractPublicKeyP *purse_pub, 112 const char *exchange_url, 113 const json_t *proof) 114 { 115 struct TALER_PurseMergeSignatureP merge_sig; 116 struct GNUNET_TIME_Timestamp merge_timestamp; 117 const char *partner_url = NULL; 118 struct TALER_ReservePublicKeyP reserve_pub; 119 struct GNUNET_JSON_Specification spec[] = { 120 GNUNET_JSON_spec_mark_optional ( 121 TALER_JSON_spec_web_url ("partner_url", 122 &partner_url), 123 NULL), 124 GNUNET_JSON_spec_timestamp ("merge_timestamp", 125 &merge_timestamp), 126 GNUNET_JSON_spec_fixed_auto ("merge_sig", 127 &merge_sig), 128 GNUNET_JSON_spec_fixed_auto ("reserve_pub", 129 &reserve_pub), 130 GNUNET_JSON_spec_end () 131 }; 132 struct TALER_NormalizedPayto payto_uri; 133 134 if (GNUNET_OK != 135 GNUNET_JSON_parse (proof, 136 spec, 137 NULL, NULL)) 138 { 139 GNUNET_break_op (0); 140 return GNUNET_SYSERR; 141 } 142 if (NULL == partner_url) 143 partner_url = exchange_url; 144 payto_uri = TALER_reserve_make_payto (partner_url, 145 &reserve_pub); 146 if (GNUNET_OK != 147 TALER_wallet_purse_merge_verify ( 148 payto_uri, 149 merge_timestamp, 150 purse_pub, 151 merge_pub, 152 &merge_sig)) 153 { 154 GNUNET_break_op (0); 155 GNUNET_free (payto_uri.normalized_payto); 156 return GNUNET_SYSERR; 157 } 158 GNUNET_free (payto_uri.normalized_payto); 159 if (0 == 160 GNUNET_memcmp (&merge_sig, 161 cmerge_sig)) 162 { 163 /* Must be the SAME data, not a conflict! */ 164 GNUNET_break_op (0); 165 return GNUNET_SYSERR; 166 } 167 return GNUNET_OK; 168 } 169 170 171 enum GNUNET_GenericReturnValue 172 TALER_EXCHANGE_check_purse_coin_conflict_ ( 173 const struct TALER_PurseContractPublicKeyP *purse_pub, 174 const char *exchange_url, 175 const json_t *proof, 176 struct TALER_DenominationHashP *h_denom_pub, 177 struct TALER_AgeCommitmentHashP *phac, 178 struct TALER_CoinSpendPublicKeyP *coin_pub, 179 struct TALER_CoinSpendSignatureP *coin_sig) 180 { 181 const char *partner_url = NULL; 182 struct TALER_Amount amount; 183 struct GNUNET_JSON_Specification spec[] = { 184 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 185 h_denom_pub), 186 GNUNET_JSON_spec_fixed_auto ("h_age_commitment", 187 phac), 188 GNUNET_JSON_spec_fixed_auto ("coin_sig", 189 coin_sig), 190 GNUNET_JSON_spec_fixed_auto ("coin_pub", 191 coin_pub), 192 GNUNET_JSON_spec_mark_optional ( 193 TALER_JSON_spec_web_url ("partner_url", 194 &partner_url), 195 NULL), 196 TALER_JSON_spec_amount_any ("amount", 197 &amount), 198 GNUNET_JSON_spec_end () 199 }; 200 201 if (GNUNET_OK != 202 GNUNET_JSON_parse (proof, 203 spec, 204 NULL, NULL)) 205 { 206 GNUNET_break_op (0); 207 return GNUNET_SYSERR; 208 } 209 if (NULL == partner_url) 210 partner_url = exchange_url; 211 if (GNUNET_OK != 212 TALER_wallet_purse_deposit_verify ( 213 partner_url, 214 purse_pub, 215 &amount, 216 h_denom_pub, 217 phac, 218 coin_pub, 219 coin_sig)) 220 { 221 GNUNET_break_op (0); 222 return GNUNET_SYSERR; 223 } 224 return GNUNET_OK; 225 } 226 227 228 enum GNUNET_GenericReturnValue 229 TALER_EXCHANGE_check_purse_econtract_conflict_ ( 230 const struct TALER_PurseContractSignatureP *ccontract_sig, 231 const struct TALER_PurseContractPublicKeyP *purse_pub, 232 const json_t *proof) 233 { 234 struct TALER_ContractDiffiePublicP contract_pub; 235 struct TALER_PurseContractSignatureP contract_sig; 236 struct GNUNET_HashCode h_econtract; 237 struct GNUNET_JSON_Specification spec[] = { 238 GNUNET_JSON_spec_fixed_auto ("h_econtract", 239 &h_econtract), 240 GNUNET_JSON_spec_fixed_auto ("econtract_sig", 241 &contract_sig), 242 GNUNET_JSON_spec_fixed_auto ("contract_pub", 243 &contract_pub), 244 GNUNET_JSON_spec_end () 245 }; 246 247 if (GNUNET_OK != 248 GNUNET_JSON_parse (proof, 249 spec, 250 NULL, NULL)) 251 { 252 GNUNET_break_op (0); 253 return GNUNET_SYSERR; 254 } 255 if (GNUNET_OK != 256 TALER_wallet_econtract_upload_verify2 ( 257 &h_econtract, 258 &contract_pub, 259 purse_pub, 260 &contract_sig)) 261 { 262 GNUNET_break_op (0); 263 return GNUNET_SYSERR; 264 } 265 if (0 == 266 GNUNET_memcmp (&contract_sig, 267 ccontract_sig)) 268 { 269 /* Must be the SAME data, not a conflict! */ 270 GNUNET_break_op (0); 271 return GNUNET_SYSERR; 272 } 273 return GNUNET_OK; 274 } 275 276 277 // FIXME: should be used... - #9422 278 enum GNUNET_GenericReturnValue 279 TALER_EXCHANGE_check_coin_denomination_conflict_ ( 280 const json_t *proof, 281 const struct TALER_DenominationHashP *ch_denom_pub) 282 { 283 struct TALER_DenominationHashP h_denom_pub; 284 struct GNUNET_JSON_Specification spec[] = { 285 GNUNET_JSON_spec_fixed_auto ("h_denom_pub", 286 &h_denom_pub), 287 GNUNET_JSON_spec_end () 288 }; 289 290 if (GNUNET_OK != 291 GNUNET_JSON_parse (proof, 292 spec, 293 NULL, NULL)) 294 { 295 GNUNET_break_op (0); 296 return GNUNET_SYSERR; 297 } 298 if (0 == 299 GNUNET_memcmp (ch_denom_pub, 300 &h_denom_pub)) 301 { 302 GNUNET_break_op (0); 303 return GNUNET_OK; 304 } 305 /* indeed, proof with different denomination key provided */ 306 return GNUNET_OK; 307 } 308 309 310 enum GNUNET_GenericReturnValue 311 TALER_EXCHANGE_get_min_denomination_ ( 312 const struct TALER_EXCHANGE_Keys *keys, 313 struct TALER_Amount *min) 314 { 315 bool have_min = false; 316 for (unsigned int i = 0; i<keys->num_denom_keys; i++) 317 { 318 const struct TALER_EXCHANGE_DenomPublicKey *dk = &keys->denom_keys[i]; 319 320 if (! have_min) 321 { 322 *min = dk->value; 323 have_min = true; 324 continue; 325 } 326 if (1 != TALER_amount_cmp (min, 327 &dk->value)) 328 continue; 329 *min = dk->value; 330 } 331 if (! have_min) 332 { 333 GNUNET_break (0); 334 return GNUNET_SYSERR; 335 } 336 return GNUNET_OK; 337 } 338 339 340 enum GNUNET_GenericReturnValue 341 TALER_EXCHANGE_verify_deposit_signature_ ( 342 const struct TALER_EXCHANGE_DepositContractDetail *dcd, 343 const struct TALER_ExtensionPolicyHashP *ech, 344 const struct TALER_MerchantWireHashP *h_wire, 345 const struct TALER_EXCHANGE_CoinDepositDetail *cdd, 346 const struct TALER_EXCHANGE_DenomPublicKey *dki) 347 { 348 if (GNUNET_OK != 349 TALER_wallet_deposit_verify (&cdd->amount, 350 &dki->fees.deposit, 351 h_wire, 352 &dcd->h_contract_terms, 353 &dcd->wallet_data_hash, 354 &cdd->h_age_commitment, 355 ech, 356 &cdd->h_denom_pub, 357 dcd->wallet_timestamp, 358 &dcd->merchant_pub, 359 dcd->refund_deadline, 360 &cdd->coin_pub, 361 &cdd->coin_sig)) 362 { 363 GNUNET_break_op (0); 364 TALER_LOG_WARNING ("Invalid coin signature on /deposit request!\n"); 365 TALER_LOG_DEBUG ("... amount_with_fee was %s\n", 366 TALER_amount2s (&cdd->amount)); 367 TALER_LOG_DEBUG ("... deposit_fee was %s\n", 368 TALER_amount2s (&dki->fees.deposit)); 369 return GNUNET_SYSERR; 370 } 371 372 /* check coin signature */ 373 { 374 struct TALER_CoinPublicInfo coin_info = { 375 .coin_pub = cdd->coin_pub, 376 .denom_pub_hash = cdd->h_denom_pub, 377 .denom_sig = cdd->denom_sig, 378 .h_age_commitment = cdd->h_age_commitment, 379 }; 380 381 if (GNUNET_YES != 382 TALER_test_coin_valid (&coin_info, 383 &dki->key)) 384 { 385 GNUNET_break_op (0); 386 TALER_LOG_WARNING ("Invalid coin passed for /deposit\n"); 387 return GNUNET_SYSERR; 388 } 389 } 390 391 /* Check coin does make a contribution */ 392 if (0 < TALER_amount_cmp (&dki->fees.deposit, 393 &cdd->amount)) 394 { 395 GNUNET_break_op (0); 396 TALER_LOG_WARNING ("Deposit amount smaller than fee\n"); 397 return GNUNET_SYSERR; 398 } 399 return GNUNET_OK; 400 } 401 402 403 /** 404 * Parse account restriction in @a jrest into @a rest. 405 * 406 * @param jresta array of account restrictions in JSON 407 * @param[out] resta_len set to length of @a resta 408 * @param[out] resta account restriction array to set 409 * @return #GNUNET_OK on success 410 */ 411 static enum GNUNET_GenericReturnValue 412 parse_restrictions (const json_t *jresta, 413 unsigned int *resta_len, 414 struct TALER_EXCHANGE_AccountRestriction **resta) 415 { 416 size_t alen; 417 418 if (! json_is_array (jresta)) 419 { 420 GNUNET_break_op (0); 421 return GNUNET_SYSERR; 422 } 423 alen = json_array_size (jresta); 424 if (0 == alen) 425 { 426 /* no restrictions, perfectly OK */ 427 *resta = NULL; 428 return GNUNET_OK; 429 } 430 *resta_len = (unsigned int) alen; 431 GNUNET_assert (alen == *resta_len); 432 *resta = GNUNET_new_array (*resta_len, 433 struct TALER_EXCHANGE_AccountRestriction); 434 for (unsigned int i = 0; i<*resta_len; i++) 435 { 436 const json_t *jr = json_array_get (jresta, 437 i); 438 struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i]; 439 const char *type = json_string_value (json_object_get (jr, 440 "type")); 441 442 if (NULL == type) 443 { 444 GNUNET_break (0); 445 goto fail; 446 } 447 if (0 == strcmp (type, 448 "deny")) 449 { 450 ar->type = TALER_EXCHANGE_AR_DENY; 451 continue; 452 } 453 if (0 == strcmp (type, 454 "regex")) 455 { 456 const char *regex; 457 const char *hint; 458 struct GNUNET_JSON_Specification spec[] = { 459 GNUNET_JSON_spec_string ( 460 "payto_regex", 461 ®ex), 462 GNUNET_JSON_spec_string ( 463 "human_hint", 464 &hint), 465 GNUNET_JSON_spec_mark_optional ( 466 GNUNET_JSON_spec_json ( 467 "human_hint_i18n", 468 &ar->details.regex.human_hint_i18n), 469 NULL), 470 GNUNET_JSON_spec_end () 471 }; 472 473 if (GNUNET_OK != 474 GNUNET_JSON_parse (jr, 475 spec, 476 NULL, NULL)) 477 { 478 /* bogus reply */ 479 GNUNET_break_op (0); 480 goto fail; 481 } 482 ar->type = TALER_EXCHANGE_AR_REGEX; 483 ar->details.regex.posix_egrep = GNUNET_strdup (regex); 484 ar->details.regex.human_hint = GNUNET_strdup (hint); 485 continue; 486 } 487 /* unsupported type */ 488 GNUNET_break (0); 489 return GNUNET_SYSERR; 490 } 491 return GNUNET_OK; 492 fail: 493 for (unsigned int i = 0; i<*resta_len; i++) 494 { 495 struct TALER_EXCHANGE_AccountRestriction *ar = &(*resta)[i]; 496 497 if (TALER_EXCHANGE_AR_REGEX == ar->type) 498 { 499 GNUNET_free (ar->details.regex.posix_egrep); 500 GNUNET_free (ar->details.regex.human_hint); 501 json_decref (ar->details.regex.human_hint_i18n); 502 } 503 } 504 GNUNET_free (*resta); 505 *resta_len = 0; 506 return GNUNET_SYSERR; 507 } 508 509 510 enum GNUNET_GenericReturnValue 511 TALER_EXCHANGE_parse_accounts ( 512 const struct TALER_MasterPublicKeyP *master_pub, 513 const json_t *accounts, 514 unsigned int was_length, 515 struct TALER_EXCHANGE_WireAccount was[static was_length]) 516 { 517 memset (was, 518 0, 519 sizeof (struct TALER_EXCHANGE_WireAccount) * was_length); 520 GNUNET_assert (was_length == 521 json_array_size (accounts)); 522 for (unsigned int i = 0; 523 i<was_length; 524 i++) 525 { 526 struct TALER_EXCHANGE_WireAccount *wa = &was[i]; 527 struct TALER_FullPayto payto_uri; 528 const char *conversion_url = NULL; 529 const char *open_banking_gateway = NULL; 530 const char *prepared_transfer_url = NULL; 531 const char *bank_label = NULL; 532 int64_t priority = 0; 533 const json_t *credit_restrictions; 534 const json_t *debit_restrictions; 535 struct GNUNET_JSON_Specification spec_account[] = { 536 TALER_JSON_spec_full_payto_uri ("payto_uri", 537 &payto_uri), 538 GNUNET_JSON_spec_mark_optional ( 539 TALER_JSON_spec_web_url ("conversion_url", 540 &conversion_url), 541 NULL), 542 GNUNET_JSON_spec_mark_optional ( 543 GNUNET_JSON_spec_string ("open_banking_gateway", 544 &open_banking_gateway), 545 NULL), 546 GNUNET_JSON_spec_mark_optional ( 547 GNUNET_JSON_spec_string ("prepared_transfer_url", 548 &prepared_transfer_url), 549 NULL), 550 GNUNET_JSON_spec_mark_optional ( 551 GNUNET_JSON_spec_int64 ("priority", 552 &priority), 553 NULL), 554 GNUNET_JSON_spec_mark_optional ( 555 GNUNET_JSON_spec_string ("bank_label", 556 &bank_label), 557 NULL), 558 GNUNET_JSON_spec_array_const ("credit_restrictions", 559 &credit_restrictions), 560 GNUNET_JSON_spec_array_const ("debit_restrictions", 561 &debit_restrictions), 562 GNUNET_JSON_spec_fixed_auto ("master_sig", 563 &wa->master_sig), 564 GNUNET_JSON_spec_end () 565 }; 566 json_t *account; 567 568 account = json_array_get (accounts, 569 i); 570 if (GNUNET_OK != 571 GNUNET_JSON_parse (account, 572 spec_account, 573 NULL, NULL)) 574 { 575 /* bogus reply */ 576 GNUNET_break_op (0); 577 return GNUNET_SYSERR; 578 } 579 if ( (NULL != master_pub) && 580 (! ( ( (NULL == open_banking_gateway) && 581 (NULL == prepared_transfer_url) && 582 (GNUNET_OK == 583 TALER_exchange_wire_signature_check32 ( 584 payto_uri, 585 conversion_url, 586 debit_restrictions, 587 credit_restrictions, 588 master_pub, 589 &wa->master_sig)) ) || 590 (GNUNET_OK == 591 TALER_exchange_wire_signature_check ( 592 payto_uri, 593 conversion_url, 594 open_banking_gateway, 595 prepared_transfer_url, 596 debit_restrictions, 597 credit_restrictions, 598 master_pub, 599 &wa->master_sig)) ) ) ) 600 { 601 /* bogus reply */ 602 GNUNET_break_op (0); 603 return GNUNET_SYSERR; 604 } 605 if ( (GNUNET_OK != 606 parse_restrictions (credit_restrictions, 607 &wa->credit_restrictions_length, 608 &wa->credit_restrictions)) || 609 (GNUNET_OK != 610 parse_restrictions (debit_restrictions, 611 &wa->debit_restrictions_length, 612 &wa->debit_restrictions)) ) 613 { 614 /* bogus reply */ 615 GNUNET_break_op (0); 616 return GNUNET_SYSERR; 617 } 618 wa->fpayto_uri.full_payto 619 = GNUNET_strdup (payto_uri.full_payto); 620 wa->priority = priority; 621 if (NULL != conversion_url) 622 wa->conversion_url = GNUNET_strdup (conversion_url); 623 if (NULL != open_banking_gateway) 624 wa->open_banking_gateway = GNUNET_strdup (open_banking_gateway); 625 if (NULL != prepared_transfer_url) 626 wa->prepared_transfer_url = GNUNET_strdup (prepared_transfer_url); 627 if (NULL != bank_label) 628 wa->bank_label = GNUNET_strdup (bank_label); 629 } /* end 'for all accounts */ 630 return GNUNET_OK; 631 } 632 633 634 /** 635 * Free array of account restrictions. 636 * 637 * @param ar_len length of @a ar 638 * @param[in] ar array to free contents of (but not @a ar itself) 639 */ 640 static void 641 free_restrictions (unsigned int ar_len, 642 struct TALER_EXCHANGE_AccountRestriction ar[static ar_len]) 643 { 644 for (unsigned int i = 0; i<ar_len; i++) 645 { 646 struct TALER_EXCHANGE_AccountRestriction *a = &ar[i]; 647 switch (a->type) 648 { 649 case TALER_EXCHANGE_AR_INVALID: 650 GNUNET_break (0); 651 break; 652 case TALER_EXCHANGE_AR_DENY: 653 break; 654 case TALER_EXCHANGE_AR_REGEX: 655 GNUNET_free (a->details.regex.posix_egrep); 656 GNUNET_free (a->details.regex.human_hint); 657 json_decref (a->details.regex.human_hint_i18n); 658 break; 659 } 660 } 661 } 662 663 664 void 665 TALER_EXCHANGE_free_accounts ( 666 unsigned int was_len, 667 struct TALER_EXCHANGE_WireAccount was[static was_len]) 668 { 669 for (unsigned int i = 0; i<was_len; i++) 670 { 671 struct TALER_EXCHANGE_WireAccount *wa = &was[i]; 672 673 GNUNET_free (wa->fpayto_uri.full_payto); 674 GNUNET_free (wa->conversion_url); 675 GNUNET_free (wa->open_banking_gateway); 676 GNUNET_free (wa->prepared_transfer_url); 677 GNUNET_free (wa->bank_label); 678 free_restrictions (wa->credit_restrictions_length, 679 wa->credit_restrictions); 680 GNUNET_array_grow (wa->credit_restrictions, 681 wa->credit_restrictions_length, 682 0); 683 free_restrictions (wa->debit_restrictions_length, 684 wa->debit_restrictions); 685 GNUNET_array_grow (wa->debit_restrictions, 686 wa->debit_restrictions_length, 687 0); 688 } 689 } 690 691 692 enum GNUNET_GenericReturnValue 693 TALER_EXCHANGE_keys_test_account_allowed ( 694 const struct TALER_EXCHANGE_Keys *keys, 695 bool check_credit, 696 const struct TALER_NormalizedPayto payto_uri) 697 { 698 /* For all accounts of the exchange */ 699 for (unsigned int i = 0; i<keys->accounts_len; i++) 700 { 701 const struct TALER_EXCHANGE_WireAccount *account 702 = &keys->accounts[i]; 703 704 /* KYC auth transfers are never supported with conversion */ 705 if (NULL != account->conversion_url) 706 continue; 707 /* filter by source account by credit_restrictions */ 708 if (GNUNET_YES != 709 TALER_EXCHANGE_test_account_allowed (account, 710 check_credit, 711 payto_uri)) 712 continue; 713 /* exchange account is allowed, add it */ 714 return true; 715 } 716 return false; 717 } 718 719 720 /** 721 * We received an #MHD_HTTP_UNAVAILABLE_FOR_LEGAL_REASONS response code. 722 * Parse the JSON response and initialize the @a uflr object. 723 * 724 * @param[out] uflr data structure to initialize 725 * @param j JSON response to parse 726 * @return #GNUNET_OK on success 727 */ 728 enum GNUNET_GenericReturnValue 729 TALER_EXCHANGE_parse_451 (struct TALER_EXCHANGE_KycNeededRedirect *uflr, 730 const json_t *j) 731 { 732 struct GNUNET_JSON_Specification spec[] = { 733 GNUNET_JSON_spec_fixed_auto ( 734 "h_payto", 735 &uflr->h_payto), 736 GNUNET_JSON_spec_uint64 ( 737 "requirement_row", 738 &uflr->requirement_row), 739 GNUNET_JSON_spec_mark_optional ( 740 GNUNET_JSON_spec_fixed_auto ( 741 "account_pub", 742 &uflr->account_pub), 743 NULL), 744 GNUNET_JSON_spec_mark_optional ( 745 GNUNET_JSON_spec_bool ( 746 "bad_kyc_auth", 747 &uflr->bad_kyc_auth), 748 NULL), 749 GNUNET_JSON_spec_end () 750 }; 751 752 if (GNUNET_OK != 753 GNUNET_JSON_parse (j, 754 spec, 755 NULL, 756 NULL)) 757 { 758 GNUNET_break_op (0); 759 return GNUNET_SYSERR; 760 } 761 return GNUNET_OK; 762 } 763 764 765 /* end of exchange_api_common.c */