anastasis_api_redux.c (68679B)
1 /* 2 This file is part of Anastasis 3 Copyright (C) 2020, 2021, 2022 Anastasis SARL 4 5 Anastasis 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 Anastasis 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 Anastasis; see the file COPYING.GPL. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file reducer/anastasis_api_redux.c 18 * @brief anastasis reducer api 19 * @author Christian Grothoff 20 * @author Dominik Meister 21 * @author Dennis Neufeld 22 */ 23 #include <platform.h> 24 #include <jansson.h> 25 #include "anastasis_redux.h" 26 #include "anastasis_error_codes.h" 27 #include <taler/taler_json_lib.h> 28 #include "anastasis_api_redux.h" 29 #include "anastasis_api_redux_state.h" 30 #include "validation.h" 31 32 33 /** 34 * How long do we wait at most for a /config reply from an Anastasis provider. 35 * 60s is very generous, given the tiny bandwidth required, even for the most 36 * remote locations. 37 */ 38 #define CONFIG_GENERIC_TIMEOUT GNUNET_TIME_UNIT_MINUTES 39 40 /** 41 * How long do we wait in a more "synchronous" 42 * scenaro for a /config reply from an Anastasis provider. 43 */ 44 #define CONFIG_FAST_TIMEOUT GNUNET_TIME_UNIT_SECONDS 45 46 47 #define GENERATE_STRING(STRING) #STRING, 48 static const char *generic_strings[] = { 49 ANASTASIS_GENERIC_STATES (GENERATE_STRING) 50 }; 51 #undef GENERATE_STRING 52 53 54 /** 55 * #ANASTASIS_REDUX_add_provider_to_state_ waiting for the 56 * configuration request to complete or fail. 57 */ 58 struct ConfigReduxWaiting 59 { 60 /** 61 * Kept in a DLL. 62 */ 63 struct ConfigReduxWaiting *prev; 64 65 /** 66 * Kept in a DLL. 67 */ 68 struct ConfigReduxWaiting *next; 69 70 /** 71 * Associated redux action. 72 */ 73 struct ANASTASIS_ReduxAction ra; 74 75 /** 76 * Config request we are waiting for. 77 */ 78 struct ConfigRequest *cr; 79 80 /** 81 * State we are processing. Borrowed, not owned: the operation that 82 * started us keeps the state alive until it has cancelled us. See 83 * #ANASTASIS_REDUX_add_provider_to_state_. 84 */ 85 struct ANASTASIS_ReduxState *rs; 86 87 /** 88 * Function to call with updated @e rs. 89 */ 90 ANASTASIS_REDUX_StateCallback cb; 91 92 /** 93 * Closure for @e cb. 94 */ 95 void *cb_cls; 96 97 }; 98 99 100 /** 101 * Anastasis authorization method configuration 102 */ 103 struct AuthorizationMethodConfig 104 { 105 /** 106 * Type of the method, i.e. "question". 107 */ 108 char *type; 109 110 /** 111 * Fee charged for accessing key share using this method, in the 112 * provider's primary currency. 113 */ 114 struct TALER_Amount usage_fee; 115 116 /** 117 * Fee charged for accessing key share using this method, per currency. 118 */ 119 struct TALER_AmountList usage_fees; 120 }; 121 122 123 /** 124 * State for a "get config" operation. 125 */ 126 struct ConfigRequest 127 { 128 129 /** 130 * Kept in a DLL, given that we may have multiple backends. 131 */ 132 struct ConfigRequest *next; 133 134 /** 135 * Kept in a DLL, given that we may have multiple backends. 136 */ 137 struct ConfigRequest *prev; 138 139 /** 140 * Head of DLL of REDUX operations waiting for an answer. 141 */ 142 struct ConfigReduxWaiting *w_head; 143 144 /** 145 * Tail of DLL of REDUX operations waiting for an answer. 146 */ 147 struct ConfigReduxWaiting *w_tail; 148 149 /** 150 * When did we start? 151 */ 152 struct GNUNET_TIME_Absolute start_time; 153 154 /** 155 * When do we time out? 156 */ 157 struct GNUNET_TIME_Absolute timeout_at; 158 159 /** 160 * How long do we wait before trying again? 161 */ 162 struct GNUNET_TIME_Relative backoff; 163 164 /** 165 * Obtained status code. 166 */ 167 unsigned int http_status; 168 169 /** 170 * The /config GET operation handle. 171 */ 172 struct ANASTASIS_ConfigOperation *co; 173 174 /** 175 * URL of the anastasis backend. 176 */ 177 char *url; 178 179 /** 180 * Business name of the anastasis backend. 181 */ 182 char *business_name; 183 184 /** 185 * Array of authorization methods supported by the server. 186 */ 187 struct AuthorizationMethodConfig *methods; 188 189 /** 190 * Length of the @e methods array. 191 */ 192 unsigned int methods_length; 193 194 /** 195 * Maximum size of an upload in megabytes. 196 */ 197 uint32_t storage_limit_in_megabytes; 198 199 /** 200 * Annual fee for an account / policy upload, primary currency. 201 */ 202 struct TALER_Amount annual_fee; 203 204 /** 205 * Annual fee for an account / policy upload, per currency. 206 */ 207 struct TALER_AmountList annual_fees; 208 209 /** 210 * Fee for a truth upload, primary currency. 211 */ 212 struct TALER_Amount truth_upload_fee; 213 214 /** 215 * Fee for a truth upload, per currency. 216 */ 217 struct TALER_AmountList truth_upload_fees; 218 219 /** 220 * Maximum legal liability for data loss covered by the 221 * provider, primary currency. 222 */ 223 struct TALER_Amount liability_limit; 224 225 /** 226 * Maximum legal liability for data loss covered by the 227 * provider, per currency. 228 */ 229 struct TALER_AmountList liability_limits; 230 231 /** 232 * Currencies the provider accepts, primary currency first. 233 */ 234 char **currencies; 235 236 /** 237 * Length of the @e currencies array. 238 */ 239 unsigned int currencies_len; 240 241 /** 242 * Provider salt. 243 */ 244 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 245 246 /** 247 * Task to timeout /config requests. 248 */ 249 struct GNUNET_SCHEDULER_Task *tt; 250 251 /** 252 * Status of the /config request. 253 */ 254 enum TALER_ErrorCode ec; 255 }; 256 257 258 /** 259 * Reducer API's CURL context handle. 260 */ 261 struct GNUNET_CURL_Context *ANASTASIS_REDUX_ctx_; 262 263 /** 264 * JSON containing country specific identity attributes to ask the user for. 265 */ 266 static json_t *redux_id_attr; 267 268 /** 269 * Head of DLL of Anastasis backend configuration requests. 270 */ 271 static struct ConfigRequest *cr_head; 272 273 /** 274 * Tail of DLL of Anastasis backend configuration requests. 275 */ 276 static struct ConfigRequest *cr_tail; 277 278 /** 279 * JSON containing country specific information. 280 */ 281 static json_t *redux_countries; 282 283 /** 284 * List of Anastasis providers. 285 */ 286 static json_t *provider_list; 287 288 /** 289 * External reducer binary or NULL 290 * to use internal reducer. 291 */ 292 static char *external_reducer_binary; 293 294 295 const char * 296 ANASTASIS_REDUX_probe_external_reducer (void) 297 { 298 if (NULL != external_reducer_binary) 299 return external_reducer_binary; 300 external_reducer_binary = getenv ("ANASTASIS_EXTERNAL_REDUCER"); 301 if (NULL != external_reducer_binary) 302 unsetenv ("ANASTASIS_EXTERNAL_REDUCER"); 303 304 return external_reducer_binary; 305 306 } 307 308 309 /** 310 * Extract the generic part of the step @a rs is at. 311 * 312 * The generic states are the common prefix of the backup and the 313 * recovery state enumerations (see #ANASTASIS_GENERIC_STATES), so a 314 * value that is in range for #ANASTASIS_GenericState means the same 315 * thing in either variant. 316 * 317 * @param rs the state to inspect 318 * @return #ANASTASIS_GENERIC_STATE_INVALID if @a rs is past the generic 319 * steps, or is an `error` state 320 */ 321 static enum ANASTASIS_GenericState 322 get_generic_state (const struct ANASTASIS_ReduxState *rs) 323 { 324 unsigned int s = UINT_MAX; 325 326 switch (rs->type) 327 { 328 case ANASTASIS_RT_BACKUP: 329 s = (unsigned int) rs->details.backup.state; 330 break; 331 case ANASTASIS_RT_RECOVERY: 332 s = (unsigned int) rs->details.recovery.state; 333 break; 334 case ANASTASIS_RT_ERROR: 335 return ANASTASIS_GENERIC_STATE_INVALID; 336 } 337 if (s >= sizeof (generic_strings) / sizeof(*generic_strings)) 338 return ANASTASIS_GENERIC_STATE_INVALID; 339 return (enum ANASTASIS_GenericState) s; 340 } 341 342 343 enum ANASTASIS_GenericState 344 ANASTASIS_generic_state_from_string_ (const char *state_string) 345 { 346 for (enum ANASTASIS_GenericState i = 0; 347 i < sizeof (generic_strings) / sizeof(*generic_strings); 348 i++) 349 if (0 == strcmp (state_string, 350 generic_strings[i])) 351 return i; 352 return ANASTASIS_GENERIC_STATE_INVALID; 353 } 354 355 356 const char * 357 ANASTASIS_generic_state_to_string_ (enum ANASTASIS_GenericState gs) 358 { 359 if ( (gs < 0) || 360 (gs >= sizeof (generic_strings) / sizeof(*generic_strings)) ) 361 { 362 GNUNET_break_op (0); 363 return NULL; 364 } 365 return generic_strings[gs]; 366 } 367 368 369 void 370 ANASTASIS_redux_fail_ (ANASTASIS_ActionCallback cb, 371 void *cb_cls, 372 enum TALER_ErrorCode ec, 373 const char *detail) 374 { 375 json_t *estate; 376 377 estate = GNUNET_JSON_PACK ( 378 GNUNET_JSON_pack_allow_null ( 379 GNUNET_JSON_pack_string ("detail", 380 detail)), 381 GNUNET_JSON_pack_string ("reducer_type", 382 "error"), 383 GNUNET_JSON_pack_uint64 ("code", 384 ec), 385 GNUNET_JSON_pack_string ("hint", 386 TALER_ErrorCode_get_hint (ec))); 387 cb (cb_cls, 388 ec, 389 estate); 390 json_decref (estate); 391 } 392 393 394 /** 395 * Transition @a rs to the generic state @a gs. 396 * 397 * @param[in,out] rs state to transition 398 * @param gs state to transition to 399 */ 400 static void 401 redux_transition (struct ANASTASIS_ReduxState *rs, 402 enum ANASTASIS_GenericState gs) 403 { 404 switch (rs->type) 405 { 406 case ANASTASIS_RT_BACKUP: 407 rs->details.backup.state = (enum ANASTASIS_BackupState) gs; 408 return; 409 case ANASTASIS_RT_RECOVERY: 410 rs->details.recovery.state = (enum ANASTASIS_RecoveryState) gs; 411 return; 412 case ANASTASIS_RT_ERROR: 413 break; 414 } 415 GNUNET_assert (0); 416 } 417 418 419 void 420 ANASTASIS_redux_init (struct GNUNET_CURL_Context *ctx) 421 { 422 ANASTASIS_REDUX_ctx_ = ctx; 423 } 424 425 426 /** 427 * Function to free a `struct ConfigRequest`, an async operation. 428 * 429 * @param cr state for a "get config" operation 430 */ 431 static void 432 free_config_request (struct ConfigRequest *cr) 433 { 434 GNUNET_assert (NULL == cr->w_head); 435 if (NULL != cr->co) 436 ANASTASIS_config_cancel (cr->co); 437 if (NULL != cr->tt) 438 GNUNET_SCHEDULER_cancel (cr->tt); 439 GNUNET_free (cr->url); 440 GNUNET_free (cr->business_name); 441 for (unsigned int i = 0; i<cr->methods_length; i++) 442 { 443 GNUNET_free (cr->methods[i].type); 444 TALER_amount_list_free (&cr->methods[i].usage_fees); 445 } 446 GNUNET_free (cr->methods); 447 TALER_amount_list_free (&cr->annual_fees); 448 TALER_amount_list_free (&cr->truth_upload_fees); 449 TALER_amount_list_free (&cr->liability_limits); 450 for (unsigned int i = 0; i<cr->currencies_len; i++) 451 GNUNET_free (cr->currencies[i]); 452 GNUNET_array_grow (cr->currencies, 453 cr->currencies_len, 454 0); 455 GNUNET_free (cr); 456 } 457 458 459 void 460 ANASTASIS_redux_done () 461 { 462 struct ConfigRequest *cr; 463 464 while (NULL != (cr = cr_head)) 465 { 466 GNUNET_CONTAINER_DLL_remove (cr_head, 467 cr_tail, 468 cr); 469 free_config_request (cr); 470 } 471 ANASTASIS_REDUX_ctx_ = NULL; 472 if (NULL != redux_countries) 473 { 474 json_decref (redux_countries); 475 redux_countries = NULL; 476 } 477 if (NULL != redux_id_attr) 478 { 479 json_decref (redux_id_attr); 480 redux_id_attr = NULL; 481 } 482 if (NULL != provider_list) 483 { 484 json_decref (provider_list); 485 provider_list = NULL; 486 } 487 } 488 489 490 const json_t * 491 ANASTASIS_redux_countries_init_ (void) 492 { 493 char *dn; 494 json_error_t error; 495 496 if (NULL != redux_countries) 497 return redux_countries; 498 499 { 500 char *path; 501 502 path = GNUNET_OS_installation_get_path (ANASTASIS_project_data (), 503 GNUNET_OS_IPK_DATADIR); 504 if (NULL == path) 505 { 506 GNUNET_break (0); 507 return NULL; 508 } 509 GNUNET_asprintf (&dn, 510 "%s/redux.countries.json", 511 path); 512 GNUNET_free (path); 513 } 514 redux_countries = json_load_file (dn, 515 JSON_COMPACT, 516 &error); 517 if (NULL == redux_countries) 518 { 519 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 520 "Failed to parse `%s': %s at %d:%d (%d)\n", 521 dn, 522 error.text, 523 error.line, 524 error.column, 525 error.position); 526 GNUNET_free (dn); 527 return NULL; 528 } 529 GNUNET_free (dn); 530 return redux_countries; 531 } 532 533 534 /** 535 * Abort waiting for /config reply. 536 * 537 * @param cls a `struct ConfigReduxWaiting` handle. 538 */ 539 static void 540 abort_provider_config_cb (void *cls) 541 { 542 struct ConfigReduxWaiting *w = cls; 543 struct ConfigRequest *cr = w->cr; 544 545 GNUNET_CONTAINER_DLL_remove (cr->w_head, 546 cr->w_tail, 547 w); 548 /* w->rs is borrowed; freeing it is the caller's business. */ 549 GNUNET_free (w); 550 } 551 552 553 /** 554 * Notify anyone waiting on @a cr that the request is done 555 * (successful or failed). 556 * 557 * @param[in,out] cr request that completed 558 */ 559 static void 560 notify_waiting (struct ConfigRequest *cr) 561 { 562 struct ConfigReduxWaiting *w; 563 564 while (NULL != (w = cr->w_head)) 565 { 566 struct ANASTASIS_ReduxProvider *p; 567 568 p = ANASTASIS_REDUX_provider_get_ (&w->rs->common, 569 cr->url); 570 if (p->have_config) 571 ANASTASIS_REDUX_provider_config_clear_ (&p->config); 572 p->have_config = false; 573 memset (&p->error, 574 0, 575 sizeof (p->error)); 576 if (TALER_EC_NONE != cr->ec) 577 { 578 p->status = ANASTASIS_RPS_ERROR; 579 p->error.ec = cr->ec; 580 p->error.http_status = cr->http_status; 581 } 582 else 583 { 584 struct ANASTASIS_ReduxProviderConfig *cfg = &p->config; 585 586 p->status = ANASTASIS_RPS_OK; 587 p->have_config = true; 588 cfg->methods_len = cr->methods_length; 589 cfg->methods = GNUNET_new_array (cfg->methods_len, 590 struct ANASTASIS_ReduxMethodSpec); 591 for (unsigned int i = 0; i<cr->methods_length; i++) 592 { 593 cfg->methods[i].type = GNUNET_strdup (cr->methods[i].type); 594 cfg->methods[i].usage_fee = cr->methods[i].usage_fee; 595 TALER_amount_list_copy (&cfg->methods[i].usage_fees, 596 &cr->methods[i].usage_fees); 597 } 598 cfg->annual_fee = cr->annual_fee; 599 TALER_amount_list_copy (&cfg->annual_fees, 600 &cr->annual_fees); 601 cfg->truth_upload_fee = cr->truth_upload_fee; 602 TALER_amount_list_copy (&cfg->truth_upload_fees, 603 &cr->truth_upload_fees); 604 cfg->liability_limit = cr->liability_limit; 605 TALER_amount_list_copy (&cfg->liability_limits, 606 &cr->liability_limits); 607 cfg->currencies_len = cr->currencies_len; 608 cfg->currencies = GNUNET_new_array (cfg->currencies_len, 609 char *); 610 for (unsigned int i = 0; i<cfg->currencies_len; i++) 611 cfg->currencies[i] = GNUNET_strdup (cr->currencies[i]); 612 if ( (NULL == cfg->currency) && 613 (0 != cfg->currencies_len) ) 614 cfg->currency = GNUNET_strdup (cfg->currencies[0]); 615 cfg->provider_salt = cr->provider_salt; 616 cfg->business_name = GNUNET_strdup (cr->business_name); 617 cfg->storage_limit_in_megabytes = cr->storage_limit_in_megabytes; 618 cfg->http_status = cr->http_status; 619 } 620 w->cb (w->cb_cls, 621 cr->ec, 622 w->rs); 623 abort_provider_config_cb (w); 624 } 625 } 626 627 628 /** 629 * Notify anyone waiting on @a cr that the request is done 630 * (successful or failed). 631 * 632 * @param[in,out] cls request that completed 633 */ 634 static void 635 notify_waiting_cb (void *cls) 636 { 637 struct ConfigRequest *cr = cls; 638 639 cr->tt = NULL; 640 notify_waiting (cr); 641 } 642 643 644 /** 645 * Function called when it is time to retry a 646 * failed /config request. 647 * 648 * @param cls the `struct ConfigRequest *` to retry. 649 */ 650 static void 651 retry_config (void *cls); 652 653 654 /** 655 * Function called with the results of a #ANASTASIS_get_config(). 656 * 657 * @param cls closure 658 * @param acfg anastasis configuration 659 */ 660 static void 661 config_cb (void *cls, 662 const struct ANASTASIS_Config *acfg) 663 { 664 struct ConfigRequest *cr = cls; 665 666 cr->co = NULL; 667 if (NULL != cr->tt) 668 { 669 GNUNET_SCHEDULER_cancel (cr->tt); 670 cr->tt = NULL; 671 } 672 cr->http_status = acfg->http_status; 673 if (MHD_HTTP_OK != acfg->http_status) 674 { 675 if (0 == acfg->http_status) 676 cr->ec = TALER_EC_ANASTASIS_GENERIC_PROVIDER_UNREACHABLE; 677 else 678 cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_CONFIG_FAILED; 679 } 680 if (0 == acfg->details.ok.storage_limit_in_megabytes) 681 { 682 cr->http_status = 0; 683 cr->ec = TALER_EC_ANASTASIS_REDUCER_PROVIDER_INVALID_CONFIG; 684 } 685 else 686 { 687 cr->ec = TALER_EC_NONE; 688 GNUNET_free (cr->business_name); 689 cr->business_name = GNUNET_strdup (acfg->details.ok.business_name); 690 for (unsigned int i = 0; i<cr->methods_length; i++) 691 { 692 GNUNET_free (cr->methods[i].type); 693 TALER_amount_list_free (&cr->methods[i].usage_fees); 694 } 695 GNUNET_free (cr->methods); 696 cr->methods = GNUNET_new_array (acfg->details.ok.methods_length, 697 struct AuthorizationMethodConfig); 698 for (unsigned int i = 0; i<acfg->details.ok.methods_length; i++) 699 { 700 cr->methods[i].type = GNUNET_strdup (acfg->details.ok.methods[i].type); 701 cr->methods[i].usage_fee = acfg->details.ok.methods[i].usage_fee; 702 TALER_amount_list_copy (&cr->methods[i].usage_fees, 703 &acfg->details.ok.methods[i].usage_fees); 704 } 705 cr->methods_length = acfg->details.ok.methods_length; 706 cr->storage_limit_in_megabytes = 707 acfg->details.ok.storage_limit_in_megabytes; 708 cr->annual_fee = acfg->details.ok.annual_fee; 709 cr->truth_upload_fee = acfg->details.ok.truth_upload_fee; 710 cr->liability_limit = acfg->details.ok.liability_limit; 711 TALER_amount_list_free (&cr->annual_fees); 712 TALER_amount_list_copy (&cr->annual_fees, 713 &acfg->details.ok.annual_fees); 714 TALER_amount_list_free (&cr->truth_upload_fees); 715 TALER_amount_list_copy (&cr->truth_upload_fees, 716 &acfg->details.ok.truth_upload_fees); 717 TALER_amount_list_free (&cr->liability_limits); 718 TALER_amount_list_copy (&cr->liability_limits, 719 &acfg->details.ok.liability_limits); 720 for (unsigned int i = 0; i<cr->currencies_len; i++) 721 GNUNET_free (cr->currencies[i]); 722 GNUNET_array_grow (cr->currencies, 723 cr->currencies_len, 724 0); 725 for (unsigned int i = 0; i<acfg->details.ok.currencies_len; i++) 726 GNUNET_array_append (cr->currencies, 727 cr->currencies_len, 728 GNUNET_strdup (acfg->details.ok.currencies[i])); 729 cr->provider_salt = acfg->details.ok.provider_salt; 730 } 731 notify_waiting (cr); 732 if (MHD_HTTP_OK != acfg->http_status) 733 { 734 cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); 735 GNUNET_assert (NULL == cr->tt); 736 GNUNET_assert (NULL != cr->url); 737 cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, 738 &retry_config, 739 cr); 740 } 741 } 742 743 744 /** 745 * Aborts a "get config" after timeout. 746 * 747 * @param cls closure for a "get config" request 748 */ 749 static void 750 config_request_timeout (void *cls) 751 { 752 struct ConfigRequest *cr = cls; 753 754 cr->tt = NULL; 755 if (NULL != cr->co) 756 { 757 ANASTASIS_config_cancel (cr->co); 758 cr->co = NULL; 759 } 760 cr->http_status = 0; 761 cr->ec = TALER_EC_GENERIC_TIMEOUT; 762 notify_waiting (cr); 763 cr->backoff = GNUNET_TIME_STD_BACKOFF (cr->backoff); 764 GNUNET_assert (NULL == cr->tt); 765 GNUNET_assert (NULL != cr->url); 766 cr->tt = GNUNET_SCHEDULER_add_delayed (cr->backoff, 767 &retry_config, 768 cr); 769 } 770 771 772 static void 773 retry_config (void *cls) 774 { 775 struct ConfigRequest *cr = cls; 776 777 cr->tt = NULL; 778 if (NULL != cr->co) 779 { 780 ANASTASIS_config_cancel (cr->co); 781 cr->co = NULL; 782 } 783 cr->timeout_at = GNUNET_TIME_relative_to_absolute (CONFIG_GENERIC_TIMEOUT); 784 GNUNET_assert (NULL == cr->tt); 785 cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, 786 &config_request_timeout, 787 cr); 788 cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, 789 cr->url, 790 &config_cb, 791 cr); 792 GNUNET_break (NULL != cr->co); 793 } 794 795 796 /** 797 * Schedule job to obtain Anastasis provider configuration at @a url. 798 * 799 * @param timeout how long to wait for a reply 800 * @param url base URL of Anastasis provider 801 * @return check config handle 802 */ 803 static struct ConfigRequest * 804 check_config (struct GNUNET_TIME_Relative timeout, 805 const char *url) 806 { 807 struct ConfigRequest *cr; 808 809 for (cr = cr_head; NULL != cr; cr = cr->next) 810 { 811 if (0 != strcmp (url, 812 cr->url)) 813 continue; 814 if (NULL != cr->co) 815 { 816 struct GNUNET_TIME_Relative duration; 817 struct GNUNET_TIME_Relative left; 818 struct GNUNET_TIME_Relative xleft; 819 820 duration = GNUNET_TIME_absolute_get_duration (cr->start_time); 821 left = GNUNET_TIME_relative_subtract (timeout, 822 duration); 823 xleft = GNUNET_TIME_absolute_get_remaining (cr->timeout_at); 824 if (GNUNET_TIME_relative_cmp (left, 825 <, 826 xleft)) 827 { 828 /* new timeout is shorter! */ 829 cr->timeout_at = GNUNET_TIME_relative_to_absolute (left); 830 GNUNET_SCHEDULER_cancel (cr->tt); 831 cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, 832 &config_request_timeout, 833 cr); 834 } 835 return cr; /* already on it */ 836 } 837 break; 838 } 839 if (NULL == cr) 840 { 841 cr = GNUNET_new (struct ConfigRequest); 842 cr->start_time = GNUNET_TIME_absolute_get (); 843 cr->url = GNUNET_strdup (url); 844 GNUNET_CONTAINER_DLL_insert (cr_head, 845 cr_tail, 846 cr); 847 } 848 if (MHD_HTTP_OK == cr->http_status) 849 return cr; 850 cr->timeout_at = GNUNET_TIME_relative_to_absolute (timeout); 851 if (NULL != cr->tt) 852 GNUNET_SCHEDULER_cancel (cr->tt); 853 cr->tt = GNUNET_SCHEDULER_add_at (cr->timeout_at, 854 &config_request_timeout, 855 cr); 856 cr->co = ANASTASIS_get_config (ANASTASIS_REDUX_ctx_, 857 cr->url, 858 &config_cb, 859 cr); 860 if (NULL == cr->co) 861 { 862 GNUNET_break (0); 863 return NULL; 864 } 865 return cr; 866 } 867 868 869 /** 870 * Begin asynchronous check for provider configurations. 871 * 872 * @param cc country code that was selected 873 * @param[in,out] common state to set the provider list for 874 * @return #TALER_EC_NONE on success 875 */ 876 static enum TALER_ErrorCode 877 begin_provider_config_check (const char *cc, 878 struct ANASTASIS_ReduxCommon *common) 879 { 880 if (NULL == provider_list) 881 { 882 json_error_t error; 883 char *dn; 884 char *path; 885 886 path = GNUNET_OS_installation_get_path (ANASTASIS_project_data (), 887 GNUNET_OS_IPK_DATADIR); 888 if (NULL == path) 889 { 890 GNUNET_break (0); 891 return TALER_EC_GENERIC_INTERNAL_INVARIANT_FAILURE; 892 } 893 GNUNET_asprintf (&dn, 894 "%s/provider-list.json", 895 path); 896 GNUNET_free (path); 897 provider_list = json_load_file (dn, 898 JSON_COMPACT, 899 &error); 900 if (NULL == provider_list) 901 { 902 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 903 "Failed to parse `%s': %s at %d:%d (%d)\n", 904 dn, 905 error.text, 906 error.line, 907 error.column, 908 error.position); 909 GNUNET_free (dn); 910 return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED; 911 } 912 GNUNET_free (dn); 913 } 914 915 { 916 size_t index; 917 json_t *provider; 918 const json_t *provider_arr = json_object_get (provider_list, 919 "anastasis_provider"); 920 921 /* The applicable providers depend on the country, so start over 922 rather than merge into whatever an earlier selection left. */ 923 ANASTASIS_REDUX_providers_clear_ (common); 924 common->have_providers = true; 925 json_array_foreach (provider_arr, index, provider) 926 { 927 const char *url; 928 const char *restricted = NULL; 929 struct GNUNET_JSON_Specification spec[] = { 930 GNUNET_JSON_spec_string ("url", 931 &url), 932 GNUNET_JSON_spec_mark_optional ( 933 GNUNET_JSON_spec_string ("restricted", 934 &restricted), 935 NULL), 936 GNUNET_JSON_spec_end () 937 }; 938 939 if (GNUNET_OK != 940 GNUNET_JSON_parse (provider, 941 spec, 942 NULL, NULL)) 943 { 944 GNUNET_break (0); 945 ANASTASIS_REDUX_providers_clear_ (common); 946 return TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED; 947 } 948 if ( (NULL != restricted) && 949 (0 != strcmp (restricted, 950 cc)) ) 951 { 952 /* skip */ 953 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 954 "Skipping provider restricted to country `%s'\n", 955 restricted); 956 continue; 957 } 958 if ( (NULL == restricted) && 959 (0 == strcmp (cc, 960 "xx")) ) 961 { 962 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 963 "Running in demo mode, skipping unrestricted providers\n"); 964 /* demo mode, skipping regular providers */ 965 continue; 966 } 967 (void) ANASTASIS_REDUX_provider_get_ (common, 968 url); 969 check_config (CONFIG_GENERIC_TIMEOUT, 970 url); 971 } 972 } 973 return TALER_EC_NONE; 974 } 975 976 977 /** 978 * Function to validate an input by regular expression ("validation-regex"). 979 * 980 * @param input text to validate 981 * @param regexp regular expression to validate 982 * @return true if validation passed, else false 983 */ 984 static bool 985 validate_regex (const char *input, 986 const char *regexp) 987 { 988 regex_t regex; 989 990 if (0 != regcomp (®ex, 991 regexp, 992 REG_EXTENDED)) 993 { 994 /* The expression comes from the (untrusted) state, so failing to compile 995 it must not mean "input accepted": that would let a client disable the 996 check for an attribute simply by supplying a broken regex. */ 997 GNUNET_break_op (0); 998 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 999 "Failed to compile regular expression `%s'.", 1000 regexp); 1001 return false; 1002 } 1003 /* check if input has correct form */ 1004 if (0 != regexec (®ex, 1005 input, 1006 0, 1007 NULL, 1008 0)) 1009 { 1010 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1011 "Input `%s' does not match regex `%s'\n", 1012 input, 1013 regexp); 1014 regfree (®ex); 1015 return false; 1016 } 1017 regfree (®ex); 1018 return true; 1019 } 1020 1021 1022 /** 1023 * Function to load json containing country specific identity 1024 * attributes. Uses a single-slot cache to avoid loading 1025 * exactly the same attributes twice. 1026 * 1027 * @param country_code country code (e.g. "de") 1028 * @return NULL on error 1029 */ 1030 static const json_t * 1031 redux_id_attr_init (const char *country_code) 1032 { 1033 static char redux_id_cc[3]; 1034 char *dn; 1035 json_error_t error; 1036 1037 /* `country_code` originates from untrusted state; it is interpolated into a 1038 file path below, so reject anything that is not exactly two ASCII letters 1039 to prevent path traversal (e.g. "../../..") into arbitrary JSON files. */ 1040 { 1041 char c0 = country_code[0]; 1042 char c1 = (c0 == '\0') ? '\0' : country_code[1]; 1043 1044 if ( (2 != strlen (country_code)) || 1045 (! ( ((c0 >= 'A') && (c0 <= 'Z')) || ((c0 >= 'a') && (c0 <= 'z')) )) || 1046 (! ( ((c1 >= 'A') && (c1 <= 'Z')) || ((c1 >= 'a') && (c1 <= 'z')) )) ) 1047 { 1048 GNUNET_break (0); 1049 return NULL; 1050 } 1051 } 1052 1053 if (0 == strcmp (country_code, 1054 redux_id_cc)) 1055 return redux_id_attr; 1056 1057 if (NULL != redux_id_attr) 1058 { 1059 json_decref (redux_id_attr); 1060 redux_id_attr = NULL; 1061 } 1062 { 1063 char *path; 1064 1065 path = GNUNET_OS_installation_get_path (ANASTASIS_project_data (), 1066 GNUNET_OS_IPK_DATADIR); 1067 if (NULL == path) 1068 { 1069 GNUNET_break (0); 1070 return NULL; 1071 } 1072 GNUNET_asprintf (&dn, 1073 "%s/redux.%s.json", 1074 path, 1075 country_code); 1076 GNUNET_free (path); 1077 } 1078 redux_id_attr = json_load_file (dn, 1079 JSON_COMPACT, 1080 &error); 1081 if (NULL == redux_id_attr) 1082 { 1083 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1084 "Failed to parse `%s': %s at %d:%d (%d)\n", 1085 dn, 1086 error.text, 1087 error.line, 1088 error.column, 1089 error.position); 1090 GNUNET_free (dn); 1091 return NULL; 1092 } 1093 GNUNET_free (dn); 1094 strncpy (redux_id_cc, 1095 country_code, 1096 sizeof (redux_id_cc)); 1097 redux_id_cc[2] = '\0'; 1098 return redux_id_attr; 1099 } 1100 1101 1102 /** 1103 * DispatchHandler/Callback function which is called for a 1104 * "select_continent" action. 1105 * 1106 * @param[in] rs state to operate on 1107 * @param arguments arguments to use for operation on state 1108 * @param cb callback to call during/after operation 1109 * @param cb_cls callback closure 1110 * @return NULL 1111 */ 1112 static struct ANASTASIS_ReduxAction * 1113 select_continent (struct ANASTASIS_ReduxState *rs, 1114 const json_t *arguments, 1115 ANASTASIS_ActionCallback cb, 1116 void *cb_cls) 1117 { 1118 const json_t *rc = ANASTASIS_redux_countries_init_ (); 1119 const json_t *root = json_object_get (rc, 1120 "countries"); 1121 const json_t *continent; 1122 const char *cname; 1123 json_t *countries; 1124 1125 if (NULL == root) 1126 { 1127 ANASTASIS_REDUX_fail_ (rs, 1128 cb, 1129 cb_cls, 1130 TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, 1131 "'countries' missing"); 1132 return NULL; 1133 } 1134 if (NULL == arguments) 1135 { 1136 ANASTASIS_REDUX_fail_ (rs, 1137 cb, 1138 cb_cls, 1139 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1140 "arguments missing"); 1141 return NULL; 1142 } 1143 continent = json_object_get (arguments, 1144 "continent"); 1145 if (NULL == continent) 1146 { 1147 ANASTASIS_REDUX_fail_ (rs, 1148 cb, 1149 cb_cls, 1150 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1151 "'continent' missing"); 1152 return NULL; 1153 } 1154 cname = json_string_value (continent); 1155 countries = json_array (); 1156 GNUNET_assert (NULL != countries); 1157 { 1158 size_t index; 1159 const json_t *country; 1160 1161 json_array_foreach (root, index, country) 1162 { 1163 const char *cc = json_string_value (json_object_get (country, 1164 "continent")); 1165 1166 if ( (NULL != cname) && 1167 (NULL != cc) && 1168 (0 == strcmp (cname, 1169 cc)) ) 1170 GNUNET_assert (0 == 1171 json_array_append (countries, 1172 (json_t *) country)); 1173 } 1174 if (0 == json_array_size (countries)) 1175 { 1176 json_decref (countries); 1177 ANASTASIS_REDUX_fail_ (rs, 1178 cb, 1179 cb_cls, 1180 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1181 "'continent' unknown"); 1182 return NULL; 1183 } 1184 } 1185 { 1186 const char *detail; 1187 1188 if (GNUNET_OK != 1189 ANASTASIS_REDUX_countries_set_ (&rs->common, 1190 countries, 1191 &detail)) 1192 { 1193 GNUNET_break (0); 1194 json_decref (countries); 1195 ANASTASIS_REDUX_fail_ (rs, 1196 cb, 1197 cb_cls, 1198 TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, 1199 detail); 1200 return NULL; 1201 } 1202 } 1203 json_decref (countries); 1204 redux_transition (rs, 1205 ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING); 1206 GNUNET_free (rs->common.selected_continent); 1207 rs->common.selected_continent = GNUNET_strdup (cname); 1208 ANASTASIS_REDUX_return_ (rs, 1209 cb, 1210 cb_cls, 1211 TALER_EC_NONE); 1212 return NULL; 1213 } 1214 1215 1216 /** 1217 * DispatchHandler/Callback function which is called for a 1218 * "select_country" action. 1219 * 1220 * @param state state to operate on 1221 * @param arguments arguments to use for operation on state 1222 * @param cb callback to call during/after operation 1223 * @param cb_cls callback closure 1224 * @return #ANASTASIS_ReduxAction 1225 */ 1226 static struct ANASTASIS_ReduxAction * 1227 select_country (struct ANASTASIS_ReduxState *rs, 1228 const json_t *arguments, 1229 ANASTASIS_ActionCallback cb, 1230 void *cb_cls) 1231 { 1232 const json_t *required_attrs; 1233 const char *country_code; 1234 1235 if (NULL == arguments) 1236 { 1237 ANASTASIS_REDUX_fail_ (rs, 1238 cb, 1239 cb_cls, 1240 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1241 "arguments missing"); 1242 return NULL; 1243 } 1244 if (NULL == json_object_get (arguments, 1245 "country_code")) 1246 { 1247 ANASTASIS_REDUX_fail_ (rs, 1248 cb, 1249 cb_cls, 1250 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1251 "'country_code' missing"); 1252 return NULL; 1253 } 1254 country_code = json_string_value (json_object_get (arguments, 1255 "country_code")); 1256 1257 { 1258 bool found = false; 1259 1260 for (unsigned int i = 0; i < rs->common.countries_len; i++) 1261 if ( (NULL != country_code) && 1262 (0 == strcmp (rs->common.countries[i].code, 1263 country_code)) ) 1264 { 1265 found = true; 1266 break; 1267 } 1268 if (! found) 1269 { 1270 ANASTASIS_REDUX_fail_ (rs, 1271 cb, 1272 cb_cls, 1273 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1274 "specified country not on selected continent"); 1275 return NULL; 1276 } 1277 } 1278 1279 /* Begin fetching provider /configs (we likely need them later) */ 1280 { 1281 enum TALER_ErrorCode ec; 1282 1283 ec = begin_provider_config_check (country_code, 1284 &rs->common); 1285 if (TALER_EC_NONE != ec) 1286 { 1287 GNUNET_break (0); 1288 ANASTASIS_REDUX_fail_ (rs, 1289 cb, 1290 cb_cls, 1291 ec, 1292 NULL); 1293 return NULL; 1294 } 1295 } 1296 1297 { 1298 const json_t *ria; 1299 1300 ria = redux_id_attr_init (country_code); 1301 if (NULL == ria) 1302 { 1303 GNUNET_break (0); 1304 ANASTASIS_REDUX_fail_ (rs, 1305 cb, 1306 cb_cls, 1307 TALER_EC_ANASTASIS_REDUCER_RESOURCE_MISSING, 1308 country_code); 1309 return NULL; 1310 } 1311 required_attrs = json_object_get (ria, 1312 "required_attributes"); 1313 } 1314 if (NULL == required_attrs) 1315 { 1316 ANASTASIS_REDUX_fail_ (rs, 1317 cb, 1318 cb_cls, 1319 TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, 1320 "'required_attributes' missing"); 1321 return NULL; 1322 } 1323 { 1324 const char *detail; 1325 1326 if (GNUNET_OK != 1327 ANASTASIS_REDUX_required_attributes_set_ (&rs->common, 1328 required_attrs, 1329 &detail)) 1330 { 1331 GNUNET_break (0); 1332 ANASTASIS_REDUX_fail_ (rs, 1333 cb, 1334 cb_cls, 1335 TALER_EC_ANASTASIS_REDUCER_RESOURCE_MALFORMED, 1336 detail); 1337 return NULL; 1338 } 1339 } 1340 redux_transition (rs, 1341 ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING); 1342 GNUNET_free (rs->common.selected_country); 1343 rs->common.selected_country = GNUNET_strdup (country_code); 1344 /* The country's currency is the default the user is offered; an 1345 explicit "select_currency" overrides it. */ 1346 if (NULL == rs->common.preferred_currency) 1347 for (unsigned int i = 0; i < rs->common.countries_len; i++) 1348 if ( (0 == strcmp (rs->common.countries[i].code, 1349 country_code)) && 1350 (NULL != rs->common.countries[i].currency) ) 1351 { 1352 rs->common.preferred_currency 1353 = GNUNET_strdup (rs->common.countries[i].currency); 1354 break; 1355 } 1356 ANASTASIS_REDUX_return_ (rs, 1357 cb, 1358 cb_cls, 1359 TALER_EC_NONE); 1360 return NULL; 1361 } 1362 1363 1364 /** 1365 * DispatchHandler/Callback function which is called for a 1366 * "select_currency" action. 1367 * 1368 * Which currency the user would rather pay in is advisory: every order 1369 * this provider creates carries all of its currencies as choices, so the 1370 * wallet may still settle in another one. What it decides is which total 1371 * the reducer quotes and which currency a UI puts in front of the user. 1372 * 1373 * @param state state to operate on 1374 * @param arguments arguments to use for operation on state 1375 * @param cb callback to call during/after operation 1376 * @param cb_cls callback closure 1377 * @return NULL 1378 */ 1379 static struct ANASTASIS_ReduxAction * 1380 select_currency (struct ANASTASIS_ReduxState *rs, 1381 const json_t *arguments, 1382 ANASTASIS_ActionCallback cb, 1383 void *cb_cls) 1384 { 1385 const char *currency; 1386 1387 if (NULL == arguments) 1388 { 1389 ANASTASIS_REDUX_fail_ (rs, 1390 cb, 1391 cb_cls, 1392 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1393 "arguments missing"); 1394 return NULL; 1395 } 1396 currency = json_string_value (json_object_get (arguments, 1397 "currency")); 1398 if (NULL == currency) 1399 { 1400 ANASTASIS_REDUX_fail_ (rs, 1401 cb, 1402 cb_cls, 1403 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1404 "'currency' missing"); 1405 return NULL; 1406 } 1407 /* Not checked against the providers: at this point their /config 1408 requests are still in flight, and a currency none of them offers is 1409 not an error either --- it just means no single-currency total can 1410 be quoted. */ 1411 if (GNUNET_OK != 1412 TALER_check_currency (currency)) 1413 { 1414 ANASTASIS_REDUX_fail_ (rs, 1415 cb, 1416 cb_cls, 1417 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1418 "'currency' malformed"); 1419 return NULL; 1420 } 1421 GNUNET_free (rs->common.preferred_currency); 1422 rs->common.preferred_currency = GNUNET_strdup (currency); 1423 ANASTASIS_REDUX_return_ (rs, 1424 cb, 1425 cb_cls, 1426 TALER_EC_NONE); 1427 return NULL; 1428 } 1429 1430 1431 /** 1432 * DispatchHandler/Callback function which is called for a 1433 * "unselect_continent" action. 1434 * 1435 * @param state state to operate on 1436 * @param arguments arguments to use for operation on state 1437 * @param cb callback to call during/after operation 1438 * @param cb_cls callback closure 1439 * @return NULL 1440 */ 1441 static struct ANASTASIS_ReduxAction * 1442 unselect_continent (struct ANASTASIS_ReduxState *rs, 1443 const json_t *arguments, 1444 ANASTASIS_ActionCallback cb, 1445 void *cb_cls) 1446 { 1447 redux_transition (rs, 1448 ANASTASIS_GENERIC_STATE_CONTINENT_SELECTING); 1449 ANASTASIS_REDUX_return_ (rs, 1450 cb, 1451 cb_cls, 1452 TALER_EC_NONE); 1453 return NULL; 1454 } 1455 1456 1457 struct ANASTASIS_ReduxAction * 1458 ANASTASIS_REDUX_add_provider_to_state_ (const char *url, 1459 struct ANASTASIS_ReduxState *rs, 1460 ANASTASIS_REDUX_StateCallback cb, 1461 void *cb_cls) 1462 { 1463 struct ConfigRequest *cr; 1464 struct ConfigReduxWaiting *w; 1465 1466 cr = check_config (CONFIG_FAST_TIMEOUT, 1467 url); 1468 w = GNUNET_new (struct ConfigReduxWaiting); 1469 w->cr = cr; 1470 w->rs = rs; 1471 w->cb = cb; 1472 w->cb_cls = cb_cls; 1473 w->ra.cleanup = &abort_provider_config_cb; 1474 w->ra.cleanup_cls = w; 1475 GNUNET_CONTAINER_DLL_insert (cr->w_head, 1476 cr->w_tail, 1477 w); 1478 if (NULL == cr->co) 1479 { 1480 if (NULL != cr->tt) 1481 GNUNET_SCHEDULER_cancel (cr->tt); 1482 cr->tt = GNUNET_SCHEDULER_add_now (¬ify_waiting_cb, 1483 cr); 1484 } 1485 return &w->ra; 1486 } 1487 1488 1489 /** 1490 * Context for #ANASTASIS_REDUX_provider_add_(). 1491 */ 1492 struct ProviderAddContext 1493 { 1494 /** 1495 * Handle we returned for cancellation of the operation. 1496 */ 1497 struct ANASTASIS_ReduxAction ra; 1498 1499 /** 1500 * The /config request we are waiting for, NULL once it completed. 1501 */ 1502 struct ANASTASIS_ReduxAction *inner; 1503 1504 /** 1505 * State we own until we hand it to @e cb. 1506 */ 1507 struct ANASTASIS_ReduxState *rs; 1508 1509 /** 1510 * Function to call with the new state. 1511 */ 1512 ANASTASIS_ActionCallback cb; 1513 1514 /** 1515 * Closure for @e cb. 1516 */ 1517 void *cb_cls; 1518 }; 1519 1520 1521 /** 1522 * Free @a cls. 1523 * 1524 * @param[in] cls a `struct ProviderAddContext *` 1525 */ 1526 static void 1527 provider_add_cleanup (void *cls) 1528 { 1529 struct ProviderAddContext *pac = cls; 1530 1531 if (NULL != pac->inner) 1532 pac->inner->cleanup (pac->inner->cleanup_cls); 1533 ANASTASIS_REDUX_state_free_ (pac->rs); 1534 GNUNET_free (pac); 1535 } 1536 1537 1538 /** 1539 * The /config request completed; return the updated state. 1540 * 1541 * @param cls a `struct ProviderAddContext *` 1542 * @param ec status of the request 1543 * @param[in] rs the updated state 1544 */ 1545 static void 1546 provider_add_done (void *cls, 1547 enum TALER_ErrorCode ec, 1548 struct ANASTASIS_ReduxState *rs) 1549 { 1550 struct ProviderAddContext *pac = cls; 1551 ANASTASIS_ActionCallback cb = pac->cb; 1552 void *cb_cls = pac->cb_cls; 1553 1554 GNUNET_assert (rs == pac->rs); 1555 /* our waiter unlinks and frees itself right after this call */ 1556 pac->inner = NULL; 1557 pac->rs = NULL; 1558 provider_add_cleanup (pac); 1559 ANASTASIS_REDUX_return_ (rs, 1560 cb, 1561 cb_cls, 1562 ec); 1563 } 1564 1565 1566 struct ANASTASIS_ReduxAction * 1567 ANASTASIS_REDUX_provider_add_ (const char *url, 1568 struct ANASTASIS_ReduxState *rs, 1569 ANASTASIS_ActionCallback cb, 1570 void *cb_cls) 1571 { 1572 struct ProviderAddContext *pac; 1573 1574 pac = GNUNET_new (struct ProviderAddContext); 1575 pac->rs = rs; 1576 pac->cb = cb; 1577 pac->cb_cls = cb_cls; 1578 pac->inner = ANASTASIS_REDUX_add_provider_to_state_ (url, 1579 rs, 1580 &provider_add_done, 1581 pac); 1582 /* the /config request never completes synchronously */ 1583 GNUNET_assert (NULL != pac->inner); 1584 pac->ra.cleanup = &provider_add_cleanup; 1585 pac->ra.cleanup_cls = pac; 1586 return &pac->ra; 1587 } 1588 1589 1590 /** 1591 * DispatchHandler/Callback function which is called for a 1592 * "enter_user_attributes" action. 1593 * Returns an #ANASTASIS_ReduxAction if operation is async. 1594 * 1595 * @param state state to operate on 1596 * @param arguments arguments to use for operation on state 1597 * @param cb callback to call during/after operation 1598 * @param cb_cls callback closure 1599 * @return NULL 1600 */ 1601 static struct ANASTASIS_ReduxAction * 1602 enter_user_attributes (struct ANASTASIS_ReduxState *rs, 1603 const json_t *arguments, 1604 ANASTASIS_ActionCallback cb, 1605 void *cb_cls) 1606 { 1607 const json_t *attributes; 1608 1609 if (NULL == arguments) 1610 { 1611 ANASTASIS_REDUX_fail_ (rs, 1612 cb, 1613 cb_cls, 1614 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1615 "arguments missing"); 1616 return NULL; 1617 } 1618 attributes = json_object_get (arguments, 1619 "identity_attributes"); 1620 if (NULL == attributes) 1621 { 1622 ANASTASIS_REDUX_fail_ (rs, 1623 cb, 1624 cb_cls, 1625 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1626 "'identity_attributes' missing"); 1627 return NULL; 1628 } 1629 json_decref (rs->common.identity_attributes); 1630 rs->common.identity_attributes = json_incref ((json_t *) attributes); 1631 1632 if (! rs->common.have_required_attributes) 1633 { 1634 ANASTASIS_REDUX_fail_ (rs, 1635 cb, 1636 cb_cls, 1637 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 1638 "'required_attributes' must be an array"); 1639 return NULL; 1640 } 1641 /* Verify required attributes are present and well-formed */ 1642 for (unsigned int i = 0; i < rs->common.required_attributes_len; i++) 1643 { 1644 const struct ANASTASIS_ReduxAttributeSpec *a 1645 = &rs->common.required_attributes[i]; 1646 const char *attribute_value; 1647 1648 attribute_value = json_string_value (json_object_get (attributes, 1649 a->name)); 1650 if (NULL == attribute_value) 1651 { 1652 if (a->optional) 1653 continue; 1654 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1655 "Request is missing required attribute `%s'\n", 1656 a->name); 1657 ANASTASIS_REDUX_fail_ (rs, 1658 cb, 1659 cb_cls, 1660 TALER_EC_GENERIC_PARAMETER_MISSING, 1661 a->name); 1662 return NULL; 1663 } 1664 if ( (NULL != a->validation_regex) && 1665 (! validate_regex (attribute_value, 1666 a->validation_regex)) ) 1667 { 1668 ANASTASIS_REDUX_fail_ (rs, 1669 cb, 1670 cb_cls, 1671 TALER_EC_ANASTASIS_REDUCER_INPUT_REGEX_FAILED, 1672 a->name); 1673 return NULL; 1674 } 1675 if (NULL != a->validation_logic) 1676 { 1677 ANASTASIS_ValidationLogic regfun; 1678 1679 regfun = ANASTASIS_REDUX_validation_lookup_ (a->validation_logic); 1680 if (NULL == regfun) 1681 { 1682 /* The name comes from the (untrusted) state, so an unknown one must 1683 fail the validation instead of skipping it. */ 1684 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1685 "Validation function `%s' is not known\n", 1686 a->validation_logic); 1687 ANASTASIS_REDUX_fail_ (rs, 1688 cb, 1689 cb_cls, 1690 TALER_EC_ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED, 1691 a->name); 1692 return NULL; 1693 } 1694 if (! regfun (attribute_value)) 1695 { 1696 ANASTASIS_REDUX_fail_ (rs, 1697 cb, 1698 cb_cls, 1699 TALER_EC_ANASTASIS_REDUCER_INPUT_VALIDATION_FAILED, 1700 a->name); 1701 return NULL; 1702 } 1703 } 1704 } /* end for all attributes loop */ 1705 1706 /* Transition based on mode */ 1707 switch (rs->type) 1708 { 1709 case ANASTASIS_RT_BACKUP: 1710 rs->details.backup.state 1711 = ANASTASIS_BACKUP_STATE_AUTHENTICATIONS_EDITING; 1712 return ANASTASIS_REDUX_backup_begin_ (rs, 1713 arguments, 1714 cb, 1715 cb_cls); 1716 case ANASTASIS_RT_RECOVERY: 1717 rs->details.recovery.state 1718 = ANASTASIS_RECOVERY_STATE_CHALLENGE_SELECTING; 1719 return ANASTASIS_REDUX_recovery_challenge_begin_ (rs, 1720 arguments, 1721 cb, 1722 cb_cls); 1723 case ANASTASIS_RT_ERROR: 1724 break; 1725 } 1726 GNUNET_assert (0); 1727 return NULL; 1728 } 1729 1730 1731 /** 1732 * DispatchHandler/Callback function which is called for a 1733 * "add_provider" action. Adds another Anastasis provider 1734 * to the list of available providers for storing information. 1735 * 1736 * @param state state to operate on 1737 * @param arguments arguments with a provider URL to add 1738 * @param cb callback to call during/after operation 1739 * @param cb_cls callback closure 1740 */ 1741 static struct ANASTASIS_ReduxAction * 1742 add_provider (struct ANASTASIS_ReduxState *rs, 1743 const json_t *arguments, 1744 ANASTASIS_ActionCallback cb, 1745 void *cb_cls) 1746 { 1747 if (ANASTASIS_add_provider_ (rs, 1748 arguments, 1749 cb, 1750 cb_cls)) 1751 return NULL; 1752 ANASTASIS_REDUX_return_ (rs, 1753 cb, 1754 cb_cls, 1755 TALER_EC_NONE); 1756 return NULL; 1757 } 1758 1759 1760 bool 1761 ANASTASIS_add_provider_ (struct ANASTASIS_ReduxState *rs, 1762 const json_t *arguments, 1763 ANASTASIS_ActionCallback cb, 1764 void *cb_cls) 1765 { 1766 const char *url; 1767 json_t *params; 1768 1769 if (NULL == arguments) 1770 { 1771 ANASTASIS_REDUX_fail_ (rs, 1772 cb, 1773 cb_cls, 1774 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1775 "arguments missing"); 1776 return true; /* cb was invoked */ 1777 } 1778 json_object_foreach (((json_t *) arguments), url, params) 1779 { 1780 const char *detail; 1781 1782 if (GNUNET_OK != 1783 ANASTASIS_REDUX_provider_set_ (&rs->common, 1784 url, 1785 params, 1786 &detail)) 1787 { 1788 GNUNET_break_op (0); 1789 ANASTASIS_REDUX_fail_ (rs, 1790 cb, 1791 cb_cls, 1792 TALER_EC_ANASTASIS_REDUCER_INPUT_INVALID, 1793 detail); 1794 return true; /* cb was invoked */ 1795 } 1796 } 1797 return false; /* cb not invoked */ 1798 } 1799 1800 1801 struct ANASTASIS_ReduxAction * 1802 ANASTASIS_back_generic_decrement_ (struct ANASTASIS_ReduxState *rs, 1803 const json_t *arguments, 1804 ANASTASIS_ActionCallback cb, 1805 void *cb_cls) 1806 { 1807 (void) arguments; 1808 switch (rs->type) 1809 { 1810 case ANASTASIS_RT_BACKUP: 1811 GNUNET_assert (rs->details.backup.state > 0); 1812 rs->details.backup.state--; 1813 break; 1814 case ANASTASIS_RT_RECOVERY: 1815 GNUNET_assert (rs->details.recovery.state > 0); 1816 rs->details.recovery.state--; 1817 break; 1818 case ANASTASIS_RT_ERROR: 1819 GNUNET_assert (0); 1820 break; 1821 } 1822 ANASTASIS_REDUX_return_ (rs, 1823 cb, 1824 cb_cls, 1825 TALER_EC_NONE); 1826 return NULL; 1827 } 1828 1829 1830 /** 1831 * Callback function which is called by the reducer in dependence of 1832 * given state and action. 1833 * 1834 * The handler takes ownership of @a rs and must dispose of it, either by 1835 * handing it to #ANASTASIS_REDUX_return_ / #ANASTASIS_REDUX_fail_ or by 1836 * passing it on to another handler that does. 1837 * 1838 * @param[in] rs the previous state to operate on 1839 * @param arguments the arguments needed by operation to operate on state 1840 * @param cb Callback function which returns the new state 1841 * @param cb_cls closure for @a cb 1842 * @return handle to cancel async actions, NULL if @a cb was already called 1843 */ 1844 typedef struct ANASTASIS_ReduxAction * 1845 (*DispatchHandler)(struct ANASTASIS_ReduxState *rs, 1846 const json_t *arguments, 1847 ANASTASIS_ActionCallback cb, 1848 void *cb_cls); 1849 1850 1851 /** 1852 * Closure for read operations on the external reducer. 1853 */ 1854 struct ExternalReducerCls 1855 { 1856 struct GNUNET_Buffer read_buffer; 1857 struct GNUNET_SCHEDULER_Task *read_task; 1858 struct GNUNET_DISK_PipeHandle *reducer_stdin; 1859 struct GNUNET_DISK_PipeHandle *reducer_stdout; 1860 struct GNUNET_Process *reducer_process; 1861 ANASTASIS_ActionCallback action_cb; 1862 void *action_cb_cls; 1863 }; 1864 1865 /** 1866 * Clean up and destroy the external reducer state. 1867 * 1868 * @param cls closure, a 'struct ExternalReducerCls *' 1869 */ 1870 static void 1871 cleanup_external_reducer (void *cls) 1872 { 1873 struct ExternalReducerCls *red_cls = cls; 1874 1875 if (NULL != red_cls->read_task) 1876 { 1877 GNUNET_SCHEDULER_cancel (red_cls->read_task); 1878 red_cls->read_task = NULL; 1879 } 1880 1881 GNUNET_buffer_clear (&red_cls->read_buffer); 1882 if (NULL != red_cls->reducer_stdin) 1883 { 1884 GNUNET_DISK_pipe_close (red_cls->reducer_stdin); 1885 red_cls->reducer_stdin = NULL; 1886 } 1887 if (NULL != red_cls->reducer_stdout) 1888 { 1889 GNUNET_DISK_pipe_close (red_cls->reducer_stdout); 1890 red_cls->reducer_stdout = NULL; 1891 } 1892 1893 if (NULL != red_cls->reducer_process) 1894 { 1895 enum GNUNET_OS_ProcessStatusType type; 1896 unsigned long code; 1897 enum GNUNET_GenericReturnValue pwret; 1898 1899 pwret = GNUNET_process_wait (red_cls->reducer_process, 1900 false, 1901 &type, 1902 &code); 1903 GNUNET_assert (GNUNET_SYSERR != pwret); 1904 if (GNUNET_NO == pwret) 1905 { 1906 GNUNET_assert (GNUNET_OK == 1907 GNUNET_process_kill (red_cls->reducer_process, 1908 SIGTERM)); 1909 GNUNET_assert (GNUNET_SYSERR != 1910 GNUNET_process_wait (red_cls->reducer_process, 1911 true, 1912 NULL, 1913 NULL)); 1914 } 1915 1916 GNUNET_process_destroy (red_cls->reducer_process); 1917 red_cls->reducer_process = NULL; 1918 } 1919 1920 GNUNET_free (red_cls); 1921 } 1922 1923 1924 /** 1925 * Task called when 1926 * 1927 * @param cls closure, a 'struct ExternalReducerCls *' 1928 */ 1929 static void 1930 external_reducer_read_cb (void *cls) 1931 { 1932 struct ExternalReducerCls *red_cls = cls; 1933 ssize_t sret; 1934 char buf[256]; 1935 1936 red_cls->read_task = NULL; 1937 1938 sret = GNUNET_DISK_file_read (GNUNET_DISK_pipe_handle ( 1939 red_cls->reducer_stdout, 1940 GNUNET_DISK_PIPE_END_READ), 1941 buf, 1942 256); 1943 if (sret < 0) 1944 { 1945 GNUNET_break (0); 1946 red_cls->action_cb (red_cls->action_cb_cls, 1947 TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR, 1948 NULL); 1949 cleanup_external_reducer (red_cls); 1950 return; 1951 } 1952 else if (0 == sret) 1953 { 1954 char *str = GNUNET_buffer_reap_str (&red_cls->read_buffer); 1955 json_t *json; 1956 1957 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 1958 "Got external reducer response: '%s'\n", 1959 str); 1960 1961 json = json_loads (str, 0, NULL); 1962 1963 if (NULL == json) 1964 { 1965 GNUNET_break (0); 1966 red_cls->action_cb (red_cls->action_cb_cls, 1967 TALER_EC_ANASTASIS_REDUCER_INTERNAL_ERROR, 1968 NULL); 1969 cleanup_external_reducer (red_cls); 1970 return; 1971 } 1972 1973 { 1974 enum TALER_ErrorCode ec; 1975 ec = json_integer_value (json_object_get (json, "code")); 1976 1977 red_cls->action_cb (red_cls->action_cb_cls, 1978 ec, 1979 json); 1980 } 1981 cleanup_external_reducer (red_cls); 1982 return; 1983 } 1984 else 1985 { 1986 GNUNET_buffer_write (&red_cls->read_buffer, 1987 buf, 1988 sret); 1989 1990 red_cls->read_task = GNUNET_SCHEDULER_add_read_file ( 1991 GNUNET_TIME_UNIT_FOREVER_REL, 1992 GNUNET_DISK_pipe_handle ( 1993 red_cls->reducer_stdout, 1994 GNUNET_DISK_PIPE_END_READ), 1995 external_reducer_read_cb, 1996 red_cls); 1997 } 1998 } 1999 2000 2001 /** 2002 * Handle an action using an external reducer, i.e. 2003 * by shelling out to another process. 2004 */ 2005 static struct ANASTASIS_ReduxAction * 2006 redux_action_external (const char *ext_reducer, 2007 const json_t *state, 2008 const char *action, 2009 const json_t *arguments, 2010 ANASTASIS_ActionCallback cb, 2011 void *cb_cls) 2012 { 2013 char *arg_str; 2014 char *state_str = json_dumps (state, JSON_COMPACT); 2015 ssize_t sret; 2016 struct ExternalReducerCls *red_cls = GNUNET_new (struct ExternalReducerCls); 2017 2018 if (NULL == arguments) 2019 arg_str = GNUNET_strdup ("{}"); 2020 else 2021 arg_str = json_dumps (arguments, JSON_COMPACT); 2022 2023 red_cls->action_cb = cb; 2024 red_cls->action_cb_cls = cb_cls; 2025 2026 GNUNET_assert (NULL != (red_cls->reducer_stdin = GNUNET_DISK_pipe ( 2027 GNUNET_DISK_PF_NONE))); 2028 GNUNET_assert (NULL != (red_cls->reducer_stdout = GNUNET_DISK_pipe ( 2029 GNUNET_DISK_PF_NONE))); 2030 2031 /* By the time we're here, this variable should be unset, because 2032 otherwise using anastasis-reducer as the external reducer 2033 will lead to infinite recursion. */ 2034 GNUNET_assert (NULL == getenv ("ANASTASIS_EXTERNAL_REDUCER")); 2035 2036 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2037 "Starting external reducer with action '%s' and argument '%s'\n", 2038 action, 2039 arg_str); 2040 2041 red_cls->reducer_process 2042 = GNUNET_process_create (GNUNET_OS_INHERIT_STD_ERR); 2043 GNUNET_assert (GNUNET_OK == 2044 GNUNET_process_set_options ( 2045 red_cls->reducer_process, 2046 GNUNET_process_option_inherit_rpipe (red_cls->reducer_stdin, 2047 STDIN_FILENO), 2048 GNUNET_process_option_inherit_rpipe (red_cls->reducer_stdout, 2049 STDOUT_FILENO))); 2050 if (GNUNET_OK != 2051 GNUNET_process_run_command_va (red_cls->reducer_process, 2052 ext_reducer, 2053 ext_reducer, 2054 "-a", 2055 arg_str, 2056 action, 2057 NULL)) 2058 { 2059 GNUNET_break (0); 2060 GNUNET_process_destroy (red_cls->reducer_process); 2061 red_cls->reducer_process = NULL; 2062 GNUNET_free (arg_str); 2063 GNUNET_free (state_str); 2064 cleanup_external_reducer (red_cls); 2065 return NULL; 2066 } 2067 GNUNET_free (arg_str); 2068 2069 /* Close pipe ends we don't use. */ 2070 GNUNET_assert (GNUNET_OK == 2071 GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin, 2072 GNUNET_DISK_PIPE_END_READ)); 2073 GNUNET_assert (GNUNET_OK == 2074 GNUNET_DISK_pipe_close_end (red_cls->reducer_stdout, 2075 GNUNET_DISK_PIPE_END_WRITE)); 2076 2077 sret = GNUNET_DISK_file_write_blocking (GNUNET_DISK_pipe_handle ( 2078 red_cls->reducer_stdin, 2079 GNUNET_DISK_PIPE_END_WRITE), 2080 state_str, 2081 strlen (state_str)); 2082 GNUNET_free (state_str); 2083 if (sret <= 0) 2084 { 2085 GNUNET_break (0); 2086 cleanup_external_reducer (red_cls); 2087 return NULL; 2088 } 2089 2090 GNUNET_assert (GNUNET_OK == 2091 GNUNET_DISK_pipe_close_end (red_cls->reducer_stdin, 2092 GNUNET_DISK_PIPE_END_WRITE)); 2093 2094 red_cls->read_task = GNUNET_SCHEDULER_add_read_file ( 2095 GNUNET_TIME_UNIT_FOREVER_REL, 2096 GNUNET_DISK_pipe_handle ( 2097 red_cls->reducer_stdout, 2098 GNUNET_DISK_PIPE_END_READ), 2099 external_reducer_read_cb, 2100 red_cls); 2101 2102 { 2103 struct ANASTASIS_ReduxAction *ra 2104 = GNUNET_new (struct ANASTASIS_ReduxAction); 2105 ra->cleanup_cls = red_cls; 2106 ra->cleanup = cleanup_external_reducer; 2107 return ra; 2108 } 2109 } 2110 2111 2112 struct ANASTASIS_ReduxAction * 2113 ANASTASIS_redux_action (const json_t *state, 2114 const char *action, 2115 const json_t *arguments, 2116 ANASTASIS_ActionCallback cb, 2117 void *cb_cls) 2118 { 2119 struct Dispatcher 2120 { 2121 enum ANASTASIS_GenericState redux_state; 2122 const char *redux_action; 2123 DispatchHandler fun; 2124 } dispatchers[] = { 2125 { 2126 ANASTASIS_GENERIC_STATE_CONTINENT_SELECTING, 2127 "select_continent", 2128 &select_continent 2129 }, 2130 /* Deprecated alias for "back" from that state, should be removed eventually. */ 2131 { 2132 ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, 2133 "unselect_continent", 2134 &unselect_continent 2135 }, 2136 { 2137 ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, 2138 "back", 2139 &unselect_continent 2140 }, 2141 { 2142 ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, 2143 "select_country", 2144 &select_country 2145 }, 2146 { 2147 ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, 2148 "select_currency", 2149 &select_currency 2150 }, 2151 { 2152 ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING, 2153 "select_currency", 2154 &select_currency 2155 }, 2156 { 2157 ANASTASIS_GENERIC_STATE_COUNTRY_SELECTING, 2158 "select_continent", 2159 &select_continent 2160 }, 2161 { 2162 ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING, 2163 "enter_user_attributes", 2164 &enter_user_attributes 2165 }, 2166 { 2167 ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING, 2168 "add_provider", 2169 &add_provider 2170 }, 2171 { 2172 ANASTASIS_GENERIC_STATE_USER_ATTRIBUTES_COLLECTING, 2173 "back", 2174 &ANASTASIS_back_generic_decrement_ 2175 }, 2176 { ANASTASIS_GENERIC_STATE_INVALID, NULL, NULL } 2177 }; 2178 struct ANASTASIS_ReduxState *rs; 2179 enum ANASTASIS_GenericState gs; 2180 2181 /* If requested, handle action with external reducer, used for testing. */ 2182 { 2183 const char *ext_reducer = ANASTASIS_REDUX_probe_external_reducer (); 2184 if (NULL != ext_reducer) 2185 return redux_action_external (ext_reducer, 2186 state, 2187 action, 2188 arguments, 2189 cb, 2190 cb_cls); 2191 } 2192 2193 /* This is the one place where a state is read. Everything below 2194 operates on the typed representation, and every path back out goes 2195 through ANASTASIS_REDUX_return_() or ANASTASIS_REDUX_fail_(). */ 2196 { 2197 enum TALER_ErrorCode ec; 2198 const char *detail; 2199 2200 rs = ANASTASIS_REDUX_state_parse_ (state, 2201 &ec, 2202 &detail); 2203 if (NULL == rs) 2204 { 2205 GNUNET_break_op (0); 2206 ANASTASIS_redux_fail_ (cb, 2207 cb_cls, 2208 ec, 2209 detail); 2210 return NULL; 2211 } 2212 } 2213 if (ANASTASIS_RT_ERROR == rs->type) 2214 { 2215 /* An error state has no step to advance from. */ 2216 GNUNET_break_op (0); 2217 ANASTASIS_REDUX_fail_ (rs, 2218 cb, 2219 cb_cls, 2220 TALER_EC_ANASTASIS_REDUCER_STATE_INVALID, 2221 "reducer_type"); 2222 return NULL; 2223 } 2224 gs = get_generic_state (rs); 2225 if (ANASTASIS_GENERIC_STATE_INVALID != gs) 2226 { 2227 for (unsigned int i = 0; NULL != dispatchers[i].fun; i++) 2228 { 2229 if ( (gs == dispatchers[i].redux_state) && 2230 (0 == strcmp (action, 2231 dispatchers[i].redux_action)) ) 2232 return dispatchers[i].fun (rs, 2233 arguments, 2234 cb, 2235 cb_cls); 2236 } 2237 } 2238 if (ANASTASIS_RT_RECOVERY == rs->type) 2239 return ANASTASIS_recovery_action_ (rs, 2240 action, 2241 arguments, 2242 cb, 2243 cb_cls); 2244 return ANASTASIS_backup_action_ (rs, 2245 action, 2246 arguments, 2247 cb, 2248 cb_cls); 2249 } 2250 2251 2252 void 2253 ANASTASIS_redux_action_cancel (struct ANASTASIS_ReduxAction *ra) 2254 { 2255 ra->cleanup (ra->cleanup_cls); 2256 } 2257 2258 2259 json_t * 2260 ANASTASIS_REDUX_load_continents_ () 2261 { 2262 const json_t *countries; 2263 json_t *continents; 2264 const json_t *rc = ANASTASIS_redux_countries_init_ (); 2265 2266 if (NULL == rc) 2267 { 2268 GNUNET_break (0); 2269 return NULL; 2270 } 2271 countries = json_object_get (rc, 2272 "countries"); 2273 if (NULL == countries) 2274 { 2275 GNUNET_break (0); 2276 return NULL; 2277 } 2278 continents = json_array (); 2279 GNUNET_assert (NULL != continents); 2280 2281 { 2282 json_t *country; 2283 size_t index; 2284 2285 json_array_foreach (countries, index, country) 2286 { 2287 json_t *ex = NULL; 2288 const json_t *continent; 2289 2290 continent = json_object_get (country, 2291 "continent"); 2292 if ( (NULL == continent) || 2293 (! json_is_string (continent)) ) 2294 { 2295 GNUNET_break (0); 2296 continue; 2297 } 2298 { 2299 size_t inner_index; 2300 json_t *inner_continent; 2301 2302 json_array_foreach (continents, inner_index, inner_continent) 2303 { 2304 const json_t *name; 2305 2306 name = json_object_get (inner_continent, 2307 "name"); 2308 if (1 == json_equal (continent, 2309 name)) 2310 { 2311 ex = inner_continent; 2312 break; 2313 } 2314 } 2315 } 2316 if (NULL == ex) 2317 { 2318 ex = GNUNET_JSON_PACK ( 2319 GNUNET_JSON_pack_string ("name", 2320 json_string_value (continent))); 2321 GNUNET_assert (0 == 2322 json_array_append_new (continents, 2323 ex)); 2324 } 2325 2326 { 2327 json_t *i18n_continent; 2328 json_t *name_ex; 2329 2330 i18n_continent = json_object_get (country, 2331 "continent_i18n"); 2332 name_ex = json_object_get (ex, 2333 "name_i18n"); 2334 if (NULL != i18n_continent) 2335 { 2336 const char *lang; 2337 json_t *trans; 2338 2339 json_object_foreach (i18n_continent, lang, trans) 2340 { 2341 if (NULL == name_ex) 2342 { 2343 name_ex = json_object (); 2344 GNUNET_assert (NULL != name_ex); 2345 GNUNET_assert (0 == 2346 json_object_set_new (ex, 2347 "name_i18n", 2348 name_ex)); 2349 } 2350 if (NULL == json_object_get (name_ex, 2351 lang)) 2352 { 2353 GNUNET_assert (0 == 2354 json_object_set (name_ex, 2355 lang, 2356 trans)); 2357 } 2358 } 2359 } 2360 } 2361 } 2362 } 2363 return GNUNET_JSON_PACK ( 2364 GNUNET_JSON_pack_array_steal ("continents", 2365 continents)); 2366 } 2367 2368 2369 enum GNUNET_GenericReturnValue 2370 ANASTASIS_REDUX_lookup_salt_ ( 2371 const struct ANASTASIS_ReduxCommon *common, 2372 const char *provider_url, 2373 struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt) 2374 { 2375 const struct ANASTASIS_ReduxProvider *p; 2376 2377 p = ANASTASIS_REDUX_provider_find_ (common, 2378 provider_url); 2379 if (NULL == p) 2380 { 2381 GNUNET_break (0); 2382 return GNUNET_SYSERR; 2383 } 2384 if (ANASTASIS_RPS_DISABLED == p->status) 2385 return GNUNET_NO; 2386 if (! p->have_config) 2387 return GNUNET_NO; 2388 *provider_salt = p->config.provider_salt; 2389 return GNUNET_OK; 2390 } 2391 2392 2393 /** 2394 * Lookup @a provider_salt of @a provider_url in @a state. 2395 * 2396 * The public API still speaks JSON here, so this is the typed lookup 2397 * with a parse in front of it. 2398 * 2399 * @param state the state to inspect 2400 * @param provider_url provider to look into 2401 * @param[out] provider_salt value to extract 2402 * @return #GNUNET_OK on success 2403 */ 2404 enum GNUNET_GenericReturnValue 2405 ANASTASIS_reducer_lookup_salt ( 2406 const json_t *state, 2407 const char *provider_url, 2408 struct ANASTASIS_CRYPTO_ProviderSaltP *provider_salt) 2409 { 2410 struct ANASTASIS_ReduxState *rs; 2411 enum GNUNET_GenericReturnValue ret; 2412 2413 { 2414 enum TALER_ErrorCode ec; 2415 const char *detail; 2416 2417 rs = ANASTASIS_REDUX_state_parse_ (state, 2418 &ec, 2419 &detail); 2420 if (NULL == rs) 2421 { 2422 GNUNET_break (0); 2423 return GNUNET_SYSERR; 2424 } 2425 } 2426 ret = ANASTASIS_REDUX_lookup_salt_ (&rs->common, 2427 provider_url, 2428 provider_salt); 2429 ANASTASIS_REDUX_state_free_ (rs); 2430 return ret; 2431 }