anastasis-gtk_action.c (105705B)
1 /* 2 This file is part of anastasis-gtk. 3 Copyright (C) 2020, 2021, 2022 Anastasis SARL 4 5 Anastasis is free software; you can redistribute it and/or modify 6 it under the terms of the GNU General Public License as published 7 by the Free Software Foundation; either version 3, or (at your 8 option) any later version. 9 10 Anastasis is distributed in the hope that it will be useful, but 11 WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 General Public License for more details. 14 15 You should have received a copy of the GNU General Public License 16 along with Anastasis; see the file COPYING. If not, write to the 17 Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 18 Boston, MA 02110-1301, USA. 19 */ 20 /** 21 * @file src/anastasis/anastasis-gtk_action.c 22 * @brief Handle redux action results 23 * @author Christian Grothoff 24 * @author Dennis Neufeld 25 */ 26 #include <gnunet/gnunet_util_lib.h> 27 #include <strings.h> 28 #include "anastasis_gtk_util.h" 29 #include "anastasis-gtk_action.h" 30 #include "anastasis-gtk_attributes.h" 31 #include "anastasis-gtk_dispatch.h" 32 #include "anastasis-gtk_helper.h" 33 #include "anastasis-gtk_handle-identity-changed.h" 34 #include "anastasis-gtk_progress.h" 35 #include <jansson.h> 36 #include <qrencode.h> 37 #include <gdk-pixbuf/gdk-pixbuf.h> 38 39 40 /** 41 * After how long does our long-poller time out? 42 */ 43 #define LP_TIMEOUT GNUNET_TIME_relative_multiply (GNUNET_TIME_UNIT_SECONDS, 30) 44 45 /** 46 * Are we currently processing an action? 47 */ 48 bool AG_in_action; 49 50 /** 51 * Are we currently editing the secret? 52 */ 53 bool AG_in_secret_editing; 54 55 /** 56 * Are we currently editing the secret name? 57 */ 58 bool AG_in_secret_name_editing; 59 60 61 #define DEBUG 0 62 63 64 /** 65 * Lookup provider name by provider URL in our state. 66 * 67 * @param provider_url URL to lookup name for 68 * @return provider name, or if unknown, the @a provider_url 69 */ 70 static const char * 71 lookup_provider_name (const char *provider_url) 72 { 73 json_t *providers; 74 json_t *provider; 75 const char *name = NULL; 76 struct GNUNET_JSON_Specification spec[] = { 77 GNUNET_JSON_spec_mark_optional ( 78 GNUNET_JSON_spec_string ("business_name", 79 &name), 80 NULL), 81 GNUNET_JSON_spec_end () 82 }; 83 84 providers = json_object_get (AG_redux_state, 85 "authentication_providers"); 86 provider = json_object_get (providers, 87 provider_url); 88 if (NULL == provider) 89 return provider_url; 90 if (GNUNET_OK != 91 GNUNET_JSON_parse (provider, 92 spec, 93 NULL, NULL)) 94 { 95 GNUNET_break (0); 96 json_dumpf (provider, 97 stderr, 98 JSON_INDENT (2)); 99 return provider_url; 100 } 101 return name; 102 } 103 104 105 /** 106 * Set country selection next-button sensitivity based on 107 * whether a country is currently selected. 108 */ 109 static void 110 set_country_selection_next (void) 111 { 112 GtkTreeView *tv; 113 GtkTreeSelection *sel; 114 GtkTreeModel *model; 115 GtkTreeIter iter; 116 117 tv = GTK_TREE_VIEW (GCG_get_main_window_object ( 118 "anastasis_gtk_country_treeview")); 119 sel = gtk_tree_view_get_selection (tv); 120 if (gtk_tree_selection_get_selected (sel, 121 &model, 122 &iter)) 123 AG_enable_next (); 124 else 125 AG_insensitive ("anastasis_gtk_main_window_forward_button"); 126 } 127 128 129 /** 130 * Prepare window for selection of the continent. 131 */ 132 static void 133 action_continent_selecting (void) 134 { 135 GtkListStore *country_liststore = GTK_LIST_STORE ( 136 GCG_get_main_window_object ("country_liststore")); 137 138 AG_hide_all_frames (); 139 gtk_list_store_clear (country_liststore); 140 { 141 GtkListStore *continent_liststore; 142 json_t *continents; 143 144 continent_liststore 145 = GTK_LIST_STORE ( 146 GCG_get_main_window_object ("continent_liststore")); 147 gtk_list_store_clear (continent_liststore); 148 continents = json_object_get (AG_redux_state, 149 "continents"); 150 if (NULL != continents) 151 { 152 json_t *continent; 153 size_t index; 154 155 json_array_foreach (continents, 156 index, 157 continent) 158 { 159 const char *name; 160 struct GNUNET_JSON_Specification spec[] = { 161 GNUNET_JSON_spec_string ("name", 162 &name), 163 GNUNET_JSON_spec_end () 164 }; 165 166 if (GNUNET_OK != 167 GNUNET_JSON_parse (continent, 168 spec, 169 NULL, NULL)) 170 { 171 GNUNET_break (0); 172 GNUNET_JSON_parse_free (spec); 173 continue; 174 } 175 176 gtk_list_store_insert_with_values (continent_liststore, 177 NULL, 178 -1, 179 AG_CMC_CONTINENT_NAME, 180 name, 181 AG_CMC_CONTINENT_NAME_I18N, 182 dgettext ("anastasis", 183 name), 184 -1); 185 GNUNET_JSON_parse_free (spec); 186 } 187 } 188 } 189 190 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 191 AG_insensitive ("anastasis_gtk_main_window_forward_button"); 192 AG_show ("anastasis_gtk_progress_vbox"); 193 AG_progress_update (); 194 if (NULL != json_object_get (AG_redux_state, 195 "backup_state")) 196 { 197 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 198 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 199 } 200 else 201 { 202 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 203 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 204 } 205 AG_show ("anastasis_gtk_main_window_prev_button"); 206 set_country_selection_next (); 207 AG_show ("anastasis_gtk_main_control_vbox"); 208 AG_show ("anastasis_gtk_continent_frame"); 209 } 210 211 212 /** 213 * Prepare window for selection of the country. 214 */ 215 static void 216 action_country_selecting (void) 217 { 218 GtkListStore *country_liststore; 219 json_t *countries; 220 const char *selected_country; 221 222 AG_hide_all_frames (); 223 countries = json_object_get (AG_redux_state, 224 "countries"); 225 selected_country 226 = json_string_value (json_object_get (AG_redux_state, 227 "selected_country")); 228 country_liststore = GTK_LIST_STORE ( 229 GCG_get_main_window_object ("country_liststore")); 230 gtk_list_store_clear (country_liststore); 231 { 232 json_t *country; 233 size_t index; 234 235 json_array_foreach (countries, index, country) 236 { 237 GtkTreeIter iter; 238 const char *code; 239 const char *name; 240 struct GNUNET_JSON_Specification spec[] = { 241 GNUNET_JSON_spec_string ("code", 242 &code), 243 GNUNET_JSON_spec_string ("name", 244 &name), 245 GNUNET_JSON_spec_end () 246 }; 247 248 if (GNUNET_OK != 249 GNUNET_JSON_parse (country, 250 spec, 251 NULL, NULL)) 252 { 253 GNUNET_break (0); 254 GNUNET_JSON_parse_free (spec); 255 continue; 256 } 257 258 gtk_list_store_insert_with_values ( 259 country_liststore, 260 &iter, 261 -1, 262 AG_CCMC_COUNTRY_NAME, 263 dgettext ("anastasis", 264 name), 265 AG_CCMC_COUNTRY_CODE, 266 code, 267 -1); 268 if ( (NULL != selected_country) && 269 (NULL != code) && 270 (0 == strcmp (code, 271 selected_country)) ) 272 { 273 GtkTreeView *tv; 274 GtkTreeSelection *sel; 275 276 tv = GTK_TREE_VIEW (GCG_get_main_window_object ( 277 "anastasis_gtk_country_treeview")); 278 sel = gtk_tree_view_get_selection (tv); 279 gtk_tree_selection_select_iter (sel, 280 &iter); 281 } 282 GNUNET_JSON_parse_free (spec); 283 } 284 } 285 286 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 287 AG_insensitive ("anastasis_gtk_main_window_forward_button"); 288 AG_show ("anastasis_gtk_main_control_vbox"); 289 AG_show ("anastasis_gtk_progress_vbox"); 290 AG_progress_update (); 291 if (NULL != json_object_get (AG_redux_state, 292 "backup_state")) 293 { 294 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 295 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 296 } 297 else 298 { 299 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 300 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 301 } 302 AG_show ("anastasis_gtk_main_window_prev_button"); 303 304 set_country_selection_next (); 305 306 AG_show ("anastasis_gtk_continent_frame"); 307 } 308 309 310 /** 311 * Create widget for "string" type user attributes. 312 * 313 * @param details not used 314 * @return widget to be used for string entry 315 */ 316 static GtkWidget * 317 ctor_entry (const json_t *details) 318 { 319 (void) details; 320 return gtk_entry_new (); 321 } 322 323 324 /** 325 * Create widget for "date" type user attributes. 326 * 327 * @param details not used 328 * @return widget to be used for date entry 329 */ 330 static GtkWidget * 331 ctor_date (const json_t *details) 332 { 333 (void) details; 334 return gtk_calendar_new (); 335 } 336 337 338 /** 339 * Create widget of @a type under @a uuid with @a label and @a tooltip 340 * for the identity attribute editing dialog. Stores all created widgets 341 * in the #AG_entry_attributes and ensures that we never create the same 342 * widget (by @a uuid) twice. 343 * 344 * @param uh hash of unique ID of the widget, only create one per UUID 345 * @param type type of the widget to create 346 * @param label label to use 347 * @param tooltip tooltip to use 348 * @param id_attr potential additional inputs for the widget creation 349 * @return created widget 350 */ 351 static GtkWidget * 352 create_attribute_widget (const struct GNUNET_HashCode *uh, 353 const char *type, 354 const char *label, 355 const char *tooltip, 356 const json_t *id_attr) 357 { 358 static struct 359 { 360 const char *type; 361 GtkWidget *(*ctor)(const json_t *details); 362 } type_map [] = { 363 { .type = "string", 364 .ctor = &ctor_entry }, 365 { .type = "date", 366 .ctor = &ctor_date }, 367 { .type = NULL, 368 .ctor = NULL } 369 }; 370 GtkWidget *w; 371 372 w = GNUNET_CONTAINER_multihashmap_get (AG_entry_attributes, 373 uh); 374 if (NULL != w) 375 { 376 GtkWidget *p; 377 378 gtk_widget_show (w); 379 p = gtk_widget_get_parent (w); 380 gtk_widget_show (p); 381 p = gtk_widget_get_parent (p); 382 gtk_widget_show (p); 383 return w; 384 } 385 for (unsigned int i = 0; NULL != type_map[i].type; i++) 386 { 387 GtkBox *box; 388 GtkBox *vbox; 389 390 if (0 != strcmp (type_map[i].type, 391 type)) 392 continue; 393 w = type_map[i].ctor (id_attr); 394 GNUNET_assert (NULL != w); 395 gtk_widget_show (w); 396 box = GTK_BOX (gtk_box_new (GTK_ORIENTATION_HORIZONTAL, 397 5 /* spacing in pixels */)); 398 vbox = GTK_BOX (gtk_box_new (GTK_ORIENTATION_VERTICAL, 399 5 /* spacing in pixels */)); 400 { 401 GtkWidget *glabel; 402 403 glabel = gtk_label_new (label); 404 gtk_box_pack_start (box, /* parent */ 405 glabel, /* child */ 406 false, /* expand */ 407 false, /* fill */ 408 5); /* padding */ 409 gtk_widget_show (glabel); 410 } 411 GNUNET_assert (0 < 412 g_signal_connect (w, 413 "changed", 414 G_CALLBACK (&AG_identity_changed), 415 NULL)); 416 GNUNET_assert (0 < 417 g_signal_connect (w, 418 "style-updated", 419 G_CALLBACK (>k_widget_queue_draw), 420 NULL)); 421 gtk_widget_set_tooltip_text (w, 422 tooltip); 423 gtk_box_pack_start (box, /* parent */ 424 w, /* child */ 425 false, /* expand */ 426 false, /* fill */ 427 5); /* padding */ 428 gtk_widget_show (GTK_WIDGET (box)); 429 gtk_box_pack_start (vbox, /* parent */ 430 GTK_WIDGET (box), /* child */ 431 false, /* expand */ 432 false, /* fill */ 433 5); /* padding */ 434 { 435 GtkWidget *private_widget; 436 GtkBuilder *builder; 437 GtkBin *bin; 438 439 builder = 440 ANASTASIS_GTK_get_new_builder ( 441 ANASTASIS_GTK_project_data (), 442 "this_stays_private.glade", 443 NULL); 444 GNUNET_break (NULL != builder); 445 /* load frame */ 446 bin = GTK_BIN (gtk_builder_get_object (builder, 447 "private_dummy_window")); 448 GNUNET_break (NULL != bin); 449 private_widget = gtk_bin_get_child (bin); 450 GNUNET_break (NULL != private_widget); 451 g_object_ref (private_widget); 452 gtk_container_remove (GTK_CONTAINER (bin), 453 private_widget); 454 gtk_widget_destroy (GTK_WIDGET (bin)); 455 g_object_unref (G_OBJECT (builder)); 456 gtk_box_pack_start (vbox, /* parent */ 457 private_widget, /* child */ 458 false, /* expand */ 459 false, /* fill */ 460 5); /* padding */ 461 } 462 gtk_widget_show (GTK_WIDGET (vbox)); 463 GNUNET_assert (GNUNET_OK == 464 GNUNET_CONTAINER_multihashmap_put (AG_entry_attributes, 465 uh, 466 w, 467 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 468 { 469 GtkBox *pbox; 470 471 pbox = GTK_BOX (GCG_get_main_window_object ( 472 "anastasis_gtk_identity_vbox")); 473 gtk_box_pack_start (pbox, /* parent */ 474 GTK_WIDGET (vbox), /* child */ 475 false, /* expand */ 476 false, /* fill */ 477 5); /* padding */ 478 479 } 480 return w; 481 } 482 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 483 "FATAL: required attribute type `%s' not supported\n", 484 type); 485 GNUNET_assert (0); 486 return NULL; 487 } 488 489 490 /** 491 * Update GtkLabel @a name, setting text to @a value. 492 * 493 * @param name Glade-name of widget to update 494 * @param value value to set 495 */ 496 static void 497 update_label (const char *name, 498 const char *value) 499 { 500 GtkLabel *label; 501 502 label = GTK_LABEL (GCG_get_main_window_object (name)); 503 if (NULL == label) 504 return; 505 if (NULL == value) 506 { 507 gtk_widget_hide (GTK_WIDGET (label)); 508 } 509 else 510 { 511 gtk_label_set_text (label, 512 value); 513 gtk_widget_show (GTK_WIDGET (label)); 514 } 515 } 516 517 518 /** 519 * We are entering the tab where the user has to enter their personal 520 * attributes. Show widgets applicable to the selected country, and 521 * dynamically generate widgets if necessary. Furthermore, restore 522 * widget contents from our state (if stored attribute values are 523 * present in our state). 524 */ 525 static void 526 action_user_attributes_collecting (void) 527 { 528 const json_t *id_attributes; 529 530 AG_hide_all_frames (); 531 id_attributes = json_object_get (AG_redux_state, 532 "required_attributes"); 533 GNUNET_assert (NULL != id_attributes); 534 AG_hide_children ("anastasis_gtk_identity_vbox"); 535 { 536 size_t index; 537 json_t *id_attr; 538 539 json_array_foreach (id_attributes, index, id_attr) 540 { 541 const char *widget_name = NULL; 542 const char *attr_tooltip = NULL; 543 const char *attr_label = NULL; 544 const char *attr_type; 545 const char *attr_uuid; 546 const char *attr_name; 547 bool optional = false; 548 struct GNUNET_JSON_Specification spec[] = { 549 GNUNET_JSON_spec_mark_optional ( 550 GNUNET_JSON_spec_string ("widget", 551 &widget_name), 552 NULL), 553 GNUNET_JSON_spec_mark_optional ( 554 GNUNET_JSON_spec_bool ("optional", 555 &optional), 556 NULL), 557 GNUNET_JSON_spec_mark_optional ( 558 GNUNET_JSON_spec_string ("tooltip", 559 &attr_tooltip), 560 NULL), 561 GNUNET_JSON_spec_string ("type", 562 &attr_type), 563 GNUNET_JSON_spec_string ("uuid", 564 &attr_uuid), 565 GNUNET_JSON_spec_string ("name", 566 &attr_name), 567 GNUNET_JSON_spec_mark_optional ( 568 GNUNET_JSON_spec_string ("label", 569 &attr_label), 570 NULL), 571 GNUNET_JSON_spec_end () 572 }; 573 struct GNUNET_HashCode uh; 574 GtkWidget *w = NULL; 575 char *l = NULL; 576 577 GNUNET_assert (GNUNET_OK == 578 GNUNET_JSON_parse (id_attr, 579 spec, 580 NULL, NULL)); 581 GNUNET_CRYPTO_hash (attr_uuid, 582 strlen (attr_uuid), 583 &uh); 584 if (NULL != widget_name) 585 { 586 char *data_name; 587 588 data_name = AG_expand_name (widget_name, 589 attr_type); 590 w = GTK_WIDGET (GCG_get_main_window_object (data_name)); 591 if (NULL == w) 592 { 593 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 594 "Widget `%s' not found, will try to create dynamic replacement\n", 595 data_name); 596 } 597 GNUNET_free (data_name); 598 } 599 if (optional) 600 GNUNET_asprintf (&l, 601 _ ("%s (optional)"), 602 dgettext ("anastasis", 603 attr_label)); 604 else 605 l = GNUNET_strdup (dgettext ("anastasis", 606 attr_label)); 607 if ( (NULL != widget_name) && 608 (NULL != w) ) 609 { 610 char *box_widget; 611 GObject *box; 612 613 GNUNET_asprintf (&box_widget, 614 "%s_box", 615 widget_name); 616 box = GCG_get_main_window_object (box_widget); 617 if (NULL == box) 618 { 619 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 620 "Widget `%s' not found, cannot show entry element. BAD.\n", 621 box_widget); 622 } 623 else 624 { 625 AG_show (box_widget); 626 AG_show_children (box_widget); 627 } 628 GNUNET_free (box_widget); 629 } 630 if ( (NULL != w) && 631 (! GNUNET_CONTAINER_multihashmap_contains (AG_entry_attributes, 632 &uh)) ) 633 { 634 GNUNET_assert (GNUNET_OK == 635 GNUNET_CONTAINER_multihashmap_put (AG_entry_attributes, 636 &uh, 637 w, 638 GNUNET_CONTAINER_MULTIHASHMAPOPTION_UNIQUE_ONLY)); 639 } 640 if (NULL == w) 641 w = create_attribute_widget (&uh, 642 attr_type, 643 l, 644 attr_tooltip, 645 id_attr); 646 GNUNET_free (l); 647 if (NULL != w) 648 { 649 json_t *ia; 650 json_t *val; 651 652 ia = json_object_get (AG_redux_state, 653 "identity_attributes"); 654 val = json_object_get (ia, 655 attr_name); 656 if ( (NULL != val) && 657 (! json_is_null (val)) ) 658 AG_import_attribute_data (w, 659 attr_type, 660 val); 661 gtk_widget_show (w); 662 } 663 GNUNET_JSON_parse_free (spec); 664 } 665 } 666 667 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 668 AG_identity_changed (); 669 AG_show ("anastasis_gtk_progress_vbox"); 670 AG_progress_update (); 671 if (NULL != json_object_get (AG_redux_state, 672 "backup_state")) 673 { 674 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 675 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 676 } 677 else 678 { 679 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 680 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 681 } 682 AG_show ("anastasis_gtk_main_window_save_as_button"); 683 AG_show ("anastasis_gtk_main_control_vbox"); 684 AG_show ("anastasis_gtk_main_window_prev_button"); 685 AG_identity_changed (); 686 AG_show ("anastasis_gtk_identity_frame"); 687 AG_focus ("anastasis_gtk_ia_full_name_entry"); 688 } 689 690 691 static void 692 activate_by_method (json_t *methods) 693 { 694 size_t index; 695 const json_t *method; 696 697 json_array_foreach (methods, 698 index, 699 method) 700 { 701 const char *type; 702 struct GNUNET_JSON_Specification spec[] = { 703 GNUNET_JSON_spec_string ("type", 704 &type), 705 GNUNET_JSON_spec_end () 706 }; 707 708 if (GNUNET_OK != 709 GNUNET_JSON_parse (method, 710 spec, 711 NULL, NULL)) 712 { 713 GNUNET_break (0); 714 json_dumpf (method, 715 stderr, 716 JSON_INDENT (2)); 717 continue; 718 } 719 720 { 721 char btn[64]; 722 723 GNUNET_snprintf (btn, 724 sizeof (btn), 725 "anastasis_gtk_btn_add_auth_%s", 726 type); 727 AG_sensitive (btn); 728 } 729 } 730 } 731 732 733 /** 734 * Function called with the results of #ANASTASIS_redux_action on "poll_providers". 735 * 736 * @param cls NULL 737 * @param error_code Error code 738 * @param response new state as result or config information of a provider 739 */ 740 static void 741 long_poll_providers_action_cb (void *cls, 742 enum TALER_ErrorCode error_code, 743 json_t *response); 744 745 746 /** 747 * Schedules the specified action. 748 * 749 * @param cls NULL 750 */ 751 static void 752 long_poll_providers_task (void *cls) 753 { 754 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_PROVIDERS]; 755 json_t *tspec; 756 757 (void) cls; 758 la->task = NULL; 759 if (GNUNET_TIME_absolute_is_future (la->next_time)) 760 { 761 la->task = GNUNET_SCHEDULER_add_at (la->next_time, 762 &long_poll_providers_task, 763 NULL); 764 return; 765 } 766 la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT); 767 tspec = GNUNET_JSON_PACK ( 768 GNUNET_JSON_pack_time_rel ("timeout", 769 LP_TIMEOUT)); 770 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 771 "Starting long polling task for provider configurations\n"); 772 la->ra = ANASTASIS_redux_action (AG_redux_state, 773 "poll_providers", 774 tspec, 775 &long_poll_providers_action_cb, 776 NULL); 777 json_decref (tspec); 778 } 779 780 781 /** 782 * Check if all providers we care about are ready, 783 * and if not try to long poll them. 784 * 785 * @return false if we have no providers at all 786 */ 787 static bool 788 sync_providers (void) 789 { 790 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_PROVIDERS]; 791 json_t *ap; 792 const char *url; 793 json_t *obj; 794 bool ready = false; 795 bool poll = false; 796 797 ap = json_object_get (AG_redux_state, 798 "authentication_providers"); 799 json_object_foreach (ap, url, obj) 800 { 801 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 802 enum GNUNET_GenericReturnValue ret; 803 804 ret = ANASTASIS_reducer_lookup_salt (AG_redux_state, 805 url, 806 &provider_salt); 807 switch (ret) 808 { 809 case GNUNET_OK: 810 ready = true; 811 break; 812 case GNUNET_NO: 813 poll = true; 814 break; 815 case GNUNET_SYSERR: 816 GNUNET_break (0); 817 break; 818 } 819 } 820 if (poll) 821 { 822 la->next_time = GNUNET_TIME_UNIT_ZERO_ABS; 823 la->task = GNUNET_SCHEDULER_add_now (&long_poll_providers_task, 824 NULL); 825 } 826 return ready || poll; 827 } 828 829 830 /** 831 * Allow the user to configure authorization methods from 832 * the set of methods offered by known providers. 833 */ 834 static void 835 action_authentications_editing (void) 836 { 837 json_t *aps; 838 bool have_auth; 839 840 AG_hide_all_frames (); 841 AG_insensitive_children ("anastasis_gtk_auth_button_grid"); 842 (void) sync_providers (); 843 aps = json_object_get (AG_redux_state, 844 "authentication_providers"); 845 { 846 const json_t *ap; 847 const char *provider_url; 848 849 json_object_foreach (aps, 850 provider_url, 851 ap) 852 { 853 uint32_t ec = 0; 854 uint32_t hc = 0; 855 const char *status; 856 json_t *methods = NULL; 857 struct GNUNET_JSON_Specification spec[] = { 858 GNUNET_JSON_spec_string ("status", 859 &status), 860 GNUNET_JSON_spec_mark_optional ( 861 GNUNET_JSON_spec_uint32 ("error_code", 862 &ec), 863 NULL), 864 GNUNET_JSON_spec_mark_optional ( 865 GNUNET_JSON_spec_json ("methods", 866 &methods), 867 NULL), 868 GNUNET_JSON_spec_mark_optional ( 869 GNUNET_JSON_spec_uint32 ("http_status", 870 &hc), 871 NULL), 872 GNUNET_JSON_spec_end () 873 }; 874 875 if (GNUNET_OK != 876 GNUNET_JSON_parse (ap, 877 spec, 878 NULL, NULL)) 879 { 880 GNUNET_break (0); 881 continue; 882 } 883 if (0 == strcmp (status, 884 "disabled")) 885 continue; 886 switch (hc) 887 { 888 case MHD_HTTP_OK: 889 if (NULL == methods) 890 { 891 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 892 "Provider `%s' has no authentication methods?\n", 893 provider_url); 894 break; 895 } 896 activate_by_method (methods); 897 break; 898 default: 899 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 900 "Status of provider `%s' is %u/%u\n", 901 provider_url, 902 (unsigned int) ec, 903 (unsigned int) hc); 904 break; 905 } 906 GNUNET_JSON_parse_free (spec); 907 } 908 } 909 910 have_auth = false; 911 { 912 GtkListStore *ls; 913 json_t *ams; 914 size_t index; 915 json_t *am; 916 917 ls = GTK_LIST_STORE (GCG_get_main_window_object ( 918 "authentication_methods_liststore")); 919 gtk_list_store_clear (ls); 920 ams = json_object_get (AG_redux_state, 921 "authentication_methods"); 922 json_array_foreach (ams, 923 index, 924 am) 925 { 926 const char *type; 927 const char *instructions; 928 struct GNUNET_JSON_Specification spec[] = { 929 GNUNET_JSON_spec_string ("type", 930 &type), 931 GNUNET_JSON_spec_string ("instructions", 932 &instructions), 933 GNUNET_JSON_spec_end () 934 }; 935 936 GNUNET_assert (GNUNET_OK == 937 GNUNET_JSON_parse (am, 938 spec, 939 NULL, NULL)); 940 gtk_list_store_insert_with_values ( 941 ls, 942 NULL, 943 -1, 944 AG_AMMC_TYPE, type, 945 AG_AMMC_VISUALIZATION, instructions, 946 AG_AMMC_INDEX, (guint) index, 947 -1); 948 have_auth = true; 949 } 950 } 951 952 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 953 if (have_auth) 954 AG_enable_next (); 955 else 956 AG_insensitive ("anastasis_gtk_main_window_forward_button"); 957 AG_show ("anastasis_gtk_progress_vbox"); 958 AG_progress_update (); 959 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 960 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 961 AG_show ("anastasis_gtk_main_control_vbox"); 962 AG_show ("anastasis_gtk_main_window_prev_button"); 963 AG_enable_next (); 964 AG_show ("anastasis_gtk_b_authentication_frame"); 965 } 966 967 968 /** 969 * Lookup @a method_cost of authentication method @a type at @a provider in our 970 * #AG_redux_state. 971 * 972 * @param provider URL of provider 973 * @param type authentication method to look for 974 * @param[out] method_cost cost to return 975 * @return #GNUNET_OK on success, #GNUNET_NO if 976 * the provider is down, #GNUNET_SYSERR on other failures 977 */ 978 static enum GNUNET_GenericReturnValue 979 lookup_recovery_cost (const char *provider, 980 const char *type, 981 struct TALER_Amount *method_cost) 982 { 983 json_t *aps; 984 json_t *ap; 985 json_t *methods; 986 size_t index; 987 json_t *method; 988 989 memset (method_cost, 990 0, 991 sizeof (struct TALER_Amount)); 992 aps = json_object_get (AG_redux_state, 993 "authentication_providers"); 994 GNUNET_assert (NULL != aps); 995 ap = json_object_get (aps, 996 provider); 997 if (NULL == ap) 998 { 999 GNUNET_break (0); 1000 json_dumpf (aps, 1001 stderr, 1002 JSON_INDENT (2)); 1003 return GNUNET_SYSERR; 1004 } 1005 methods = json_object_get (ap, 1006 "methods"); 1007 if (NULL == methods) 1008 return GNUNET_NO; /* provider down */ 1009 json_array_foreach (methods, index, method) 1010 { 1011 struct TALER_Amount fee; 1012 const char *mtype; 1013 struct GNUNET_JSON_Specification spec[] = { 1014 TALER_JSON_spec_amount_any ("usage_fee", 1015 &fee), 1016 GNUNET_JSON_spec_string ("type", 1017 &mtype), 1018 GNUNET_JSON_spec_end () 1019 }; 1020 1021 if (GNUNET_OK != 1022 GNUNET_JSON_parse (method, 1023 spec, 1024 NULL, NULL)) 1025 { 1026 GNUNET_break (0); 1027 json_dumpf (method, 1028 stderr, 1029 JSON_INDENT (2)); 1030 continue; 1031 } 1032 if (0 == strcmp (mtype, 1033 type)) 1034 { 1035 *method_cost = fee; 1036 return GNUNET_OK; 1037 } 1038 } 1039 GNUNET_break (0); 1040 json_dumpf (methods, 1041 stderr, 1042 JSON_INDENT (2)); 1043 return GNUNET_SYSERR; 1044 } 1045 1046 1047 static void 1048 action_policies_reviewing (void) 1049 { 1050 json_t *policies; 1051 size_t pindex; 1052 json_t *policy; 1053 GtkTreeStore *ts; 1054 1055 AG_hide_all_frames (); 1056 ts = GTK_TREE_STORE (GCG_get_main_window_object ("policy_review_treestore")); 1057 gtk_tree_store_clear (ts); 1058 policies = json_object_get (AG_redux_state, 1059 "policies"); 1060 GNUNET_assert (NULL != policies); 1061 json_array_foreach (policies, pindex, policy) 1062 { 1063 GtkTreeIter piter; 1064 json_t *methods; 1065 struct GNUNET_JSON_Specification pspec[] = { 1066 GNUNET_JSON_spec_json ("methods", 1067 &methods), 1068 GNUNET_JSON_spec_end () 1069 }; 1070 size_t mindex; 1071 json_t *method; 1072 char *summary; 1073 1074 if (GNUNET_OK != 1075 GNUNET_JSON_parse (policy, 1076 pspec, 1077 NULL, NULL)) 1078 { 1079 GNUNET_break (0); 1080 continue; 1081 } 1082 gtk_tree_store_insert_with_values (ts, 1083 &piter, 1084 NULL, /* no parent */ 1085 -1, /* append */ 1086 -1); 1087 1088 summary = NULL; 1089 json_array_foreach (methods, mindex, method) 1090 { 1091 uint32_t imethod; 1092 const char *provider_url; 1093 struct GNUNET_JSON_Specification mspec[] = { 1094 GNUNET_JSON_spec_string ("provider", 1095 &provider_url), 1096 GNUNET_JSON_spec_uint32 ("authentication_method", 1097 &imethod), 1098 GNUNET_JSON_spec_end () 1099 }; 1100 json_t *jmethods; 1101 json_t *jmethod; 1102 1103 if (GNUNET_OK != 1104 GNUNET_JSON_parse (method, 1105 mspec, 1106 NULL, NULL)) 1107 { 1108 json_dumpf (method, 1109 stderr, 1110 JSON_INDENT (2)); 1111 GNUNET_break (0); 1112 continue; 1113 } 1114 jmethods = json_object_get (AG_redux_state, 1115 "authentication_methods"); 1116 jmethod = json_array_get (jmethods, 1117 imethod); 1118 { 1119 GtkTreeIter miter; 1120 const char *instructions; 1121 const char *type; 1122 struct GNUNET_JSON_Specification tspec[] = { 1123 GNUNET_JSON_spec_string ("instructions", 1124 &instructions), 1125 GNUNET_JSON_spec_string ("type", 1126 &type), 1127 GNUNET_JSON_spec_end () 1128 }; 1129 struct TALER_Amount method_cost; 1130 const char *provider_name; 1131 1132 if (GNUNET_OK != 1133 GNUNET_JSON_parse (jmethod, 1134 tspec, 1135 NULL, NULL)) 1136 { 1137 GNUNET_break (0); 1138 continue; 1139 } 1140 if (GNUNET_OK != 1141 lookup_recovery_cost (provider_url, 1142 type, 1143 &method_cost)) 1144 { 1145 GNUNET_break (0); 1146 continue; 1147 } 1148 provider_name = lookup_provider_name (provider_url); 1149 gtk_tree_store_insert_with_values ( 1150 ts, 1151 &miter, 1152 &piter, /* parent */ 1153 -1, /* append */ 1154 AG_PRMC_POLICY_NAME, 1155 instructions, 1156 AG_PRMC_METHOD_TYPE, 1157 type, 1158 AG_PRMC_COST, 1159 TALER_amount2s (&method_cost), 1160 AG_PRMC_PROVIDER_URL, 1161 provider_url, 1162 AG_PRMC_EXPIRATION_TIME_STR, 1163 "N/A", 1164 AG_PRMC_POLICY_INDEX, 1165 (guint) pindex, 1166 AG_PRMC_IS_CHALLENGE, 1167 TRUE, 1168 AG_PRMC_METHOD_INDEX, 1169 (guint) mindex, 1170 AG_PRMC_PROVIDER_NAME, 1171 provider_name, 1172 -1); 1173 if (NULL == summary) 1174 { 1175 summary = GNUNET_strdup (type); 1176 } 1177 else 1178 { 1179 char *tmp; 1180 1181 GNUNET_asprintf (&tmp, 1182 "%s + %s", 1183 summary, 1184 type); 1185 GNUNET_free (summary); 1186 summary = tmp; 1187 } 1188 } 1189 GNUNET_JSON_parse_free (mspec); 1190 } 1191 if (NULL != summary) 1192 { 1193 gtk_tree_store_set (ts, 1194 &piter, 1195 AG_PRMC_POLICY_NAME, summary, 1196 AG_PRMC_EXPIRATION_TIME_STR, 1197 "N/A", 1198 AG_PRMC_POLICY_INDEX, 1199 (guint) pindex, 1200 AG_PRMC_IS_CHALLENGE, 1201 FALSE, 1202 -1); 1203 GNUNET_free (summary); 1204 } 1205 GNUNET_JSON_parse_free (pspec); 1206 } 1207 { 1208 GtkTreeView *tv; 1209 1210 tv = GTK_TREE_VIEW (GCG_get_main_window_object ( 1211 "anastasis_gtk_review_policy_treeview")); 1212 gtk_tree_view_expand_all (tv); 1213 } 1214 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 1215 AG_show ("anastasis_gtk_progress_vbox"); 1216 AG_progress_update (); 1217 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 1218 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 1219 AG_show ("anastasis_gtk_b_policy_frame"); 1220 AG_show ("anastasis_gtk_main_control_vbox"); 1221 AG_show ("anastasis_gtk_main_window_prev_button"); 1222 AG_enable_next (); 1223 } 1224 1225 1226 /** 1227 * Update GtkEntry @a name, setting text to @a value. 1228 * 1229 * @param name Glade-name of widget to update 1230 * @param value value to set 1231 */ 1232 static void 1233 update_entry (const char *name, 1234 const char *value) 1235 { 1236 GtkEntry *entry; 1237 const char *old; 1238 1239 if (NULL == value) 1240 value = ""; 1241 entry = GTK_ENTRY (GCG_get_main_window_object (name)); 1242 if (NULL == entry) 1243 { 1244 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 1245 "`%s' is not a GtkEntry!\n", 1246 name); 1247 return; 1248 } 1249 old = gtk_entry_get_text (entry); 1250 if (NULL == old) 1251 old = ""; 1252 if (0 != strcmp (old, 1253 value)) 1254 gtk_entry_set_text (entry, 1255 value); 1256 } 1257 1258 1259 /** 1260 * Function called when we begin editing the secret. 1261 */ 1262 static void 1263 action_secret_editing (void) 1264 { 1265 struct GNUNET_TIME_Timestamp exp_time; 1266 struct GNUNET_JSON_Specification spec[] = { 1267 GNUNET_JSON_spec_timestamp ("expiration", 1268 &exp_time), 1269 GNUNET_JSON_spec_end () 1270 }; 1271 struct tm tv; 1272 bool is_free = false; 1273 1274 AG_hide_all_frames (); 1275 if (GNUNET_OK != 1276 GNUNET_JSON_parse (AG_redux_state, 1277 spec, 1278 NULL, NULL)) 1279 { 1280 GNUNET_break (0); 1281 AG_error ("State did not parse correctly: lacks expiration"); 1282 return; 1283 } 1284 1285 { 1286 time_t t; 1287 1288 t = exp_time.abs_time.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us; 1289 GNUNET_assert (NULL != 1290 localtime_r (&t, 1291 &tv)); 1292 } 1293 1294 { 1295 json_t *fees; 1296 1297 fees = json_object_get (AG_redux_state, 1298 "upload_fees"); 1299 if (0 == json_array_size (fees)) 1300 { 1301 update_label ("backup_fee_value_label", 1302 _ ("gratis")); 1303 is_free = true; 1304 } 1305 else 1306 { 1307 char *val = GNUNET_strdup (""); 1308 size_t pos; 1309 json_t *fee; 1310 struct TALER_Amount a; 1311 1312 json_array_foreach (fees, pos, fee) 1313 { 1314 struct GNUNET_JSON_Specification ispec[] = { 1315 TALER_JSON_spec_amount_any ("fee", 1316 &a), 1317 GNUNET_JSON_spec_end () 1318 }; 1319 char *tmp; 1320 1321 if (GNUNET_OK != 1322 GNUNET_JSON_parse (fee, 1323 ispec, 1324 NULL, NULL)) 1325 { 1326 GNUNET_break (0); 1327 json_dumpf (fees, 1328 stderr, 1329 JSON_INDENT (2)); 1330 continue; 1331 } 1332 1333 GNUNET_asprintf (&tmp, 1334 "%s%s%llu.%u %s", 1335 val, 1336 strlen (val) > 0 ? "\n" : "", 1337 (unsigned long long) a.value, 1338 (unsigned int) a.fraction, 1339 a.currency); 1340 GNUNET_free (val); 1341 val = tmp; 1342 } 1343 update_label ("backup_fee_value_label", 1344 val); 1345 GNUNET_free (val); 1346 } 1347 } 1348 { 1349 char estr[128]; 1350 1351 if (is_free) 1352 GNUNET_assert (sizeof (estr) > 1353 strftime (estr, 1354 sizeof (estr), 1355 "%d %B %Y", 1356 &tv)); 1357 else 1358 GNUNET_assert (sizeof (estr) > 1359 strftime (estr, 1360 sizeof (estr), 1361 "%d %B", 1362 &tv)); 1363 update_label ("expiration_date_without_year_label", 1364 estr); 1365 } 1366 1367 { 1368 GtkSpinButton *sb; 1369 unsigned int this_year; 1370 unsigned int exp_year; 1371 1372 sb = GTK_SPIN_BUTTON (GCG_get_main_window_object ( 1373 "expiration_year_spin_button")); 1374 if (is_free) 1375 gtk_widget_hide (GTK_WIDGET (sb)); 1376 else 1377 gtk_widget_show (GTK_WIDGET (sb)); 1378 this_year = GNUNET_TIME_get_current_year (); 1379 /* We allow at most 5 years into the future */ 1380 gtk_spin_button_set_range (sb, 1381 this_year + 1, 1382 this_year + 6); 1383 exp_year = tv.tm_year + 1900; 1384 gtk_spin_button_set_value (sb, 1385 (double) exp_year); 1386 } 1387 1388 AG_insensitive ("anastasis_gtk_main_window_forward_button"); 1389 AG_sensitive ("anastasis_gtk_enter_secret_open_button"); 1390 AG_sensitive ("anastasis_gtk_enter_secret_textview"); 1391 AG_hide ("anastasis_gtk_secret_clear_file_button"); 1392 AG_hide ("anastasis_gtk_secret_clear_text_button"); 1393 AG_hide ("anastasis_gtk_secret_file_name_hbox"); 1394 AG_show ("anastasis_gtk_secret_file_chooser_hbox"); 1395 { 1396 const char *name = ""; 1397 const json_t *jsecret = NULL; 1398 const char *filename = NULL; 1399 struct GNUNET_JSON_Specification ispec[] = { 1400 GNUNET_JSON_spec_mark_optional ( 1401 GNUNET_JSON_spec_object_const ("core_secret", 1402 &jsecret), 1403 NULL), 1404 GNUNET_JSON_spec_mark_optional ( 1405 GNUNET_JSON_spec_string ("secret_name", 1406 &name), 1407 NULL), 1408 GNUNET_JSON_spec_end () 1409 }; 1410 1411 if (GNUNET_OK != 1412 GNUNET_JSON_parse (AG_redux_state, 1413 ispec, 1414 NULL, NULL)) 1415 { 1416 GNUNET_break (0); 1417 json_dumpf (AG_redux_state, 1418 stderr, 1419 JSON_INDENT (2)); 1420 AG_error ("State did not parse correctly: invalid secret data"); 1421 return; 1422 } 1423 if (! AG_in_secret_name_editing) 1424 update_entry ("anastasis_gtk_secret_name_entry", 1425 name); 1426 if (NULL != jsecret) 1427 { 1428 const char *mime = NULL; 1429 const char *text = NULL; 1430 struct GNUNET_JSON_Specification sspec[] = { 1431 GNUNET_JSON_spec_mark_optional ( 1432 GNUNET_JSON_spec_string ("text", 1433 &text), 1434 NULL), 1435 GNUNET_JSON_spec_mark_optional ( 1436 GNUNET_JSON_spec_string ("mime", 1437 &mime), 1438 NULL), 1439 GNUNET_JSON_spec_mark_optional ( 1440 GNUNET_JSON_spec_string ("filename", 1441 &filename), 1442 NULL), 1443 GNUNET_JSON_spec_end () 1444 }; 1445 1446 if (GNUNET_OK != 1447 GNUNET_JSON_parse (jsecret, 1448 sspec, 1449 NULL, NULL)) 1450 { 1451 GNUNET_break (0); 1452 json_dumpf (AG_redux_state, 1453 stderr, 1454 JSON_INDENT (2)); 1455 AG_error ("State did not parse correctly: invalid secret data"); 1456 return; 1457 } 1458 if ( (NULL != text) && 1459 (0 == strlen (text)) ) 1460 text = NULL; 1461 if (! AG_in_secret_editing) 1462 { 1463 GtkTextBuffer *tb = GTK_TEXT_BUFFER (GCG_get_main_window_object ( 1464 "anastasis_gtk_enter_secret_textbuffer")); 1465 const char *old; 1466 GtkTextIter start; 1467 GtkTextIter end; 1468 1469 gtk_text_buffer_get_start_iter (tb, 1470 &start); 1471 gtk_text_buffer_get_end_iter (tb, 1472 &end); 1473 old = gtk_text_buffer_get_text (tb, 1474 &start, 1475 &end, 1476 true); 1477 if (0 != strcmp (old, 1478 NULL != text 1479 ? text 1480 : "")) 1481 gtk_text_buffer_set_text (tb, 1482 NULL != text 1483 ? text 1484 : "", 1485 -1); 1486 } 1487 update_label ("anastasis_gtk_secret_file_name_label", 1488 filename); 1489 if ( (NULL != text) || 1490 (NULL != filename) ) 1491 { 1492 AG_enable_next (); 1493 } 1494 if (NULL != text) 1495 { 1496 AG_insensitive ("anastasis_gtk_enter_secret_open_button"); 1497 AG_show ("anastasis_gtk_secret_clear_text_button"); 1498 } 1499 if (NULL != filename) 1500 { 1501 AG_insensitive ("anastasis_gtk_enter_secret_textview"); 1502 AG_show ("anastasis_gtk_secret_clear_file_button"); 1503 AG_show ("anastasis_gtk_secret_file_name_hbox"); 1504 AG_hide ("anastasis_gtk_secret_file_chooser_hbox"); 1505 } 1506 GNUNET_JSON_parse_free (sspec); 1507 } 1508 else 1509 { 1510 /* secret is NULL */ 1511 GtkTextBuffer *tb = GTK_TEXT_BUFFER (GCG_get_main_window_object ( 1512 "anastasis_gtk_enter_secret_textbuffer")); 1513 1514 gtk_text_buffer_set_text (tb, 1515 "", 1516 -1); 1517 } 1518 if ( (NULL == name) || 1519 (0 == strlen (name) ) ) 1520 AG_focus ("anastasis_gtk_secret_name_entry"); 1521 else if (NULL == filename) 1522 AG_focus ("anastasis_gtk_enter_secret_textview"); 1523 } 1524 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 1525 AG_show ("anastasis_gtk_progress_vbox"); 1526 AG_progress_update (); 1527 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 1528 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 1529 AG_show ("anastasis_gtk_enter_secret_frame"); 1530 AG_show ("anastasis_gtk_main_control_vbox"); 1531 AG_show ("anastasis_gtk_main_window_prev_button"); 1532 AG_enable_next (); 1533 } 1534 1535 1536 static void 1537 action_truths_paying (void) 1538 { 1539 json_t *pprs; 1540 size_t index; 1541 json_t *pt; 1542 GtkListStore *ls; 1543 1544 AG_hide_all_frames (); 1545 ls = GTK_LIST_STORE (GCG_get_main_window_object ( 1546 "unpaid_qrcodes_liststore")); 1547 gtk_list_store_clear (ls); 1548 pprs = json_object_get (AG_redux_state, 1549 "payments"); 1550 json_array_foreach (pprs, index, pt) 1551 { 1552 const char *payto = json_string_value (pt); 1553 GdkPixbuf *pb; 1554 GtkWidget *w; 1555 1556 w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview")); 1557 if (NULL == w) 1558 { 1559 GNUNET_break (0); 1560 continue; 1561 } 1562 if (NULL == payto) 1563 { 1564 GNUNET_break (0); 1565 continue; 1566 } 1567 pb = AG_setup_qrcode (w, 1568 payto, 1569 strlen (payto)); 1570 if (NULL == pb) 1571 { 1572 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1573 _ ("Failed to initialize QR-code pixbuf for `%s'\n"), 1574 payto); 1575 continue; 1576 } 1577 1578 gtk_list_store_insert_with_values (ls, 1579 NULL, 1580 -1, /* append */ 1581 AG_UQRMC_QR_IMAGE, pb, 1582 AG_UQRMC_URL, payto, 1583 AG_UQRMC_PROVIDER, "", 1584 -1); 1585 g_object_unref (pb); 1586 } 1587 1588 { 1589 json_t *args; 1590 struct GNUNET_TIME_Relative timeout; 1591 1592 timeout = GNUNET_TIME_UNIT_MINUTES; 1593 GNUNET_assert (NULL == AG_ra); 1594 args = GNUNET_JSON_PACK ( 1595 GNUNET_JSON_pack_time_rel ("timeout", 1596 timeout)); 1597 AG_ra = ANASTASIS_redux_action (AG_redux_state, 1598 "pay", 1599 args, 1600 &AG_action_cb, 1601 NULL); 1602 json_decref (args); 1603 } 1604 AG_show ("anastasis_gtk_pay_frame"); 1605 AG_show ("anastasis_gtk_main_control_vbox"); 1606 AG_show ("anastasis_gtk_progress_vbox"); 1607 AG_progress_update (); 1608 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 1609 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 1610 AG_hide ("anastasis_gtk_main_window_prev_button"); 1611 AG_hide ("anastasis_gtk_main_window_forward_button"); 1612 } 1613 1614 1615 static void 1616 action_policies_paying (void) 1617 { 1618 json_t *pprs; 1619 size_t index; 1620 json_t *ppr; 1621 GtkListStore *ls; 1622 1623 AG_hide_all_frames (); 1624 ls = GTK_LIST_STORE (GCG_get_main_window_object ( 1625 "unpaid_qrcodes_liststore")); 1626 gtk_list_store_clear (ls); 1627 pprs = json_object_get (AG_redux_state, 1628 "policy_payment_requests"); 1629 json_array_foreach (pprs, index, ppr) 1630 { 1631 const char *provider; 1632 const char *payto; 1633 struct GNUNET_JSON_Specification spec[] = { 1634 GNUNET_JSON_spec_string ("provider", 1635 &provider), 1636 GNUNET_JSON_spec_string ("payto", 1637 &payto), 1638 GNUNET_JSON_spec_end () 1639 }; 1640 GdkPixbuf *pb; 1641 GtkWidget *w; 1642 1643 w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview")); 1644 if (NULL == w) 1645 { 1646 GNUNET_break (0); 1647 continue; 1648 } 1649 if (GNUNET_OK != 1650 GNUNET_JSON_parse (ppr, 1651 spec, 1652 NULL, NULL)) 1653 { 1654 GNUNET_break (0); 1655 continue; 1656 } 1657 pb = AG_setup_qrcode (w, 1658 payto, 1659 strlen (payto)); 1660 if (NULL == pb) 1661 { 1662 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 1663 _ ("Failed to initialize QR-code pixbuf for `%s'\n"), 1664 payto); 1665 continue; 1666 } 1667 1668 gtk_list_store_insert_with_values (ls, 1669 NULL, 1670 -1, /* append */ 1671 AG_UQRMC_QR_IMAGE, pb, 1672 AG_UQRMC_URL, payto, 1673 AG_UQRMC_PROVIDER, provider, 1674 -1); 1675 g_object_unref (pb); 1676 } 1677 { 1678 json_t *args; 1679 struct GNUNET_TIME_Relative timeout; 1680 1681 timeout = GNUNET_TIME_UNIT_MINUTES; 1682 GNUNET_assert (NULL == AG_ra); 1683 args = GNUNET_JSON_PACK ( 1684 GNUNET_JSON_pack_time_rel ("timeout", 1685 timeout)); 1686 AG_ra = ANASTASIS_redux_action (AG_redux_state, 1687 "pay", 1688 args, 1689 &AG_action_cb, 1690 NULL); 1691 json_decref (args); 1692 } 1693 AG_show ("anastasis_gtk_pay_frame"); 1694 AG_show ("anastasis_gtk_main_control_vbox"); 1695 AG_show ("anastasis_gtk_progress_vbox"); 1696 AG_progress_update (); 1697 AG_show ("anastasis_gtk_backup_progress_scrolled_window"); 1698 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 1699 AG_hide ("anastasis_gtk_main_window_prev_button"); 1700 AG_hide ("anastasis_gtk_main_window_forward_button"); 1701 } 1702 1703 1704 /** 1705 * The backup has finished, show the providers, policy version and 1706 * expiration dates. 1707 */ 1708 static void 1709 action_backup_finished (void) 1710 { 1711 json_t *det; 1712 json_t *se; 1713 const char *url; 1714 GtkListStore *ls; 1715 struct GNUNET_TIME_Timestamp mexp; 1716 1717 AG_hide_all_frames (); 1718 det = json_object_get (AG_redux_state, 1719 "success_details"); 1720 ls = GTK_LIST_STORE (GCG_get_main_window_object ( 1721 "backup_provider_liststore")); 1722 gtk_list_store_clear (ls); 1723 mexp = GNUNET_TIME_UNIT_FOREVER_TS; 1724 json_object_foreach (det, url, se) 1725 { 1726 struct GNUNET_TIME_Timestamp pexp; 1727 uint64_t version; 1728 struct GNUNET_JSON_Specification spec[] = { 1729 GNUNET_JSON_spec_uint64 ("policy_version", 1730 &version), 1731 GNUNET_JSON_spec_timestamp ("policy_expiration", 1732 &pexp), 1733 GNUNET_JSON_spec_end () 1734 }; 1735 1736 if (GNUNET_OK != 1737 GNUNET_JSON_parse (se, 1738 spec, 1739 NULL, NULL)) 1740 { 1741 GNUNET_break_op (0); 1742 AG_error ("State did not parse correctly"); 1743 return; 1744 } 1745 mexp = GNUNET_TIME_timestamp_min (mexp, 1746 pexp); 1747 gtk_list_store_insert_with_values ( 1748 ls, 1749 NULL, 1750 -1, /* append */ 1751 AG_BPC_PROVIDER_URL, 1752 url, 1753 AG_BPC_BACKUP_VERSION, 1754 (guint64) version, 1755 AG_BPC_EXPIRATION_TIME_STR, 1756 GNUNET_TIME_timestamp2s (pexp), 1757 AG_BPC_SUCCESS_FLAG, 1758 true, 1759 AG_BPC_PROVIDER_NAME, 1760 lookup_provider_name (url), 1761 -1); 1762 } 1763 { 1764 struct tm tv; 1765 char estr[128]; 1766 time_t t; 1767 struct GNUNET_TIME_Absolute sexp; 1768 1769 /* be more conservative in what we show */ 1770 sexp = GNUNET_TIME_absolute_subtract (mexp.abs_time, 1771 GNUNET_TIME_UNIT_DAYS); 1772 t = sexp.abs_value_us / GNUNET_TIME_UNIT_SECONDS.rel_value_us; 1773 GNUNET_assert (NULL != 1774 localtime_r (&t, 1775 &tv)); 1776 GNUNET_assert (sizeof (estr) > 1777 strftime (estr, 1778 sizeof (estr), 1779 "%d %B %Y", 1780 &tv)); 1781 update_label ("backup_expiration_date_label", 1782 GNUNET_TIME_absolute2s (sexp)); 1783 } 1784 AG_hide ("anastasis_gtk_progress_vbox"); 1785 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 1786 AG_show ("anastasis_gtk_completed_frame"); 1787 AG_show ("anastasis_gtk_backup_complete_box"); 1788 AG_hide ("anastasis_gtk_success_recovery_box"); 1789 AG_show ("anastasis_gtk_success_backup_label"); 1790 AG_hide ("anastasis_gtk_success_recovery_box"); 1791 AG_show ("anastasis_gtk_main_control_vbox"); 1792 AG_hide ("anastasis_gtk_main_window_save_as_button"); 1793 AG_show ("anastasis_gtk_restart_button"); 1794 AG_show ("anastasis_gtk_main_window_quit_button"); 1795 AG_hide ("anastasis_gtk_main_window_prev_button"); 1796 AG_hide ("anastasis_gtk_main_window_forward_button"); 1797 } 1798 1799 1800 static const json_t * 1801 find_challenge_by_uuid (const char *uuid) 1802 { 1803 json_t *rd; 1804 json_t *cs; 1805 size_t index; 1806 json_t *c; 1807 1808 rd = json_object_get (AG_redux_state, 1809 "recovery_document"); 1810 cs = json_object_get (rd, 1811 "challenges"); 1812 json_array_foreach (cs, index, c) 1813 { 1814 const char *u; 1815 1816 u = json_string_value (json_object_get (c, 1817 "uuid")); 1818 if (NULL == u) 1819 { 1820 GNUNET_break (0); 1821 continue; 1822 } 1823 if (0 == strcmp (u, 1824 uuid)) 1825 return c; 1826 } 1827 return NULL; 1828 } 1829 1830 1831 /** 1832 * Update the list store with the challenge feedback. 1833 * 1834 * @param uuid the UUID to render feedback for 1835 * @param builder the builder to use to grab display widgets 1836 */ 1837 static void 1838 show_challenge_feedback (const char *uuid, 1839 GtkBuilder *builder) 1840 { 1841 json_t *cf; 1842 const json_t *f; 1843 const char *state; 1844 const char *hint = NULL; 1845 const char *taler_pay_uri = NULL; 1846 uint32_t ec = TALER_EC_NONE; 1847 struct GNUNET_JSON_Specification spec[] = { 1848 GNUNET_JSON_spec_string ("state", 1849 &state), 1850 GNUNET_JSON_spec_mark_optional ( 1851 GNUNET_JSON_spec_string ("taler_pay_uri", 1852 &taler_pay_uri), 1853 NULL), 1854 GNUNET_JSON_spec_mark_optional ( 1855 GNUNET_JSON_spec_string ("display_hint", 1856 &hint), 1857 NULL), 1858 GNUNET_JSON_spec_mark_optional ( 1859 GNUNET_JSON_spec_uint32 ("error_code", 1860 &ec), 1861 NULL), 1862 GNUNET_JSON_spec_end () 1863 }; 1864 1865 cf = json_object_get (AG_redux_state, 1866 "challenge_feedback"); 1867 f = json_object_get (cf, uuid); 1868 if (NULL == f) 1869 return; /* no feedback */ 1870 if (GNUNET_OK != 1871 GNUNET_JSON_parse (f, 1872 spec, 1873 NULL, NULL)) 1874 { 1875 GNUNET_break (0); 1876 json_dumpf (f, 1877 stderr, 1878 JSON_INDENT (2)); 1879 return; 1880 } 1881 if (0) 1882 { 1883 /* FIXME: do we want to show the payment request here at all? */ 1884 if (NULL != taler_pay_uri) 1885 { 1886 GdkPixbuf *qr; 1887 GtkWidget *w = NULL; 1888 1889 qr = AG_setup_qrcode (w, 1890 taler_pay_uri, 1891 strlen (taler_pay_uri)); 1892 g_object_unref (qr); 1893 } 1894 } 1895 if (TALER_EC_NONE != ec) 1896 { 1897 const char *emsg; 1898 GtkLabel *l; 1899 char *msg; 1900 1901 emsg = TALER_ErrorCode_get_hint (ec); 1902 GNUNET_asprintf (&msg, 1903 "#%u: %s", 1904 (unsigned int) ec, 1905 emsg); 1906 l = GTK_LABEL (gtk_builder_get_object (builder, 1907 "error_label")); 1908 gtk_label_set_text (l, 1909 msg); 1910 GNUNET_free (msg); 1911 gtk_widget_show (GTK_WIDGET (l)); 1912 } 1913 if (NULL != hint) 1914 { 1915 GtkLabel *l; 1916 1917 l = GTK_LABEL (gtk_builder_get_object (builder, 1918 "hint_label")); 1919 gtk_label_set_text (l, 1920 hint); 1921 gtk_widget_show (GTK_WIDGET (l)); 1922 } 1923 GNUNET_JSON_parse_free (spec); 1924 } 1925 1926 1927 /** 1928 * Function called on each discovered recovery policy. Called 1929 * with all arguments NULL if we have received all policies that 1930 * we could possibly receive for the current operation. 1931 * 1932 * The client can then start a new policy discovery process, using the 1933 * smallest (also most recent) @a version received per @a provider_url 1934 * in the cursor to resume. Note that in this case, the application 1935 * logic is responsible for de-duplication using @a hcpd, or it may show 1936 * policies again if they are at different providers under versions not 1937 * queried up to the cursor. 1938 * 1939 * @param cls closure with the `GtkListStore` to update. 1940 * @param hcpd hash of the compressed policy document (unique per policy) 1941 * @param provider_url which provider claims to have this policy 1942 * @param version version of the policy at this provider 1943 * @param attribute_mask combination of optional identity attributes 1944 * present in the state that was used to locate this version 1945 * @param server_time when did the provider receive the upload 1946 * @param secret_name name the user assigned to the backup 1947 * @param providers json array of providers offering this policy 1948 */ 1949 static void 1950 expand_policy_list (void *cls, 1951 const struct GNUNET_HashCode *hcpd, 1952 const char *provider_url, 1953 uint32_t version, 1954 json_int_t attribute_mask, 1955 struct GNUNET_TIME_Timestamp server_time, 1956 const char *secret_name, 1957 const json_t *providers) 1958 { 1959 GtkListStore *ls = cls; 1960 GtkTreeIter iter; 1961 bool have_first; 1962 1963 have_first = gtk_tree_model_get_iter_first (GTK_TREE_MODEL (ls), 1964 &iter); 1965 gtk_list_store_insert_with_values ( 1966 ls, 1967 &iter, 1968 -1, /* append */ 1969 AG_SSMC_PROVIDER_URL, provider_url, 1970 AG_SSMC_POLICY_VERSION, (gint) version, 1971 AG_SSMC_ATTRIBUTE_MASK, (gint) attribute_mask, 1972 AG_SSMC_SECRET_NAME, secret_name, 1973 AG_SSMC_POLICY_DATE_STRING, GNUNET_TIME_timestamp2s (server_time), 1974 AG_SSMC_POLICY_DATE_NUMERIC, (guint64) server_time.abs_time.abs_value_us, 1975 AG_SSMC_POLICY_PROVIDER_JSON, (gpointer) providers, 1976 -1); 1977 if (! have_first) 1978 { 1979 GtkTreeSelection *selection; 1980 1981 selection = GTK_TREE_SELECTION ( 1982 GCG_get_main_window_object ( 1983 "anastasis_gtk_secret_selection_treeselection")); 1984 gtk_tree_selection_select_iter (selection, 1985 &iter); 1986 } 1987 } 1988 1989 1990 /** 1991 * Function called with the results of #ANASTASIS_redux_action on "poll". 1992 * 1993 * @param cls NULL 1994 * @param error_code Error code 1995 * @param response new state as result or config information of a provider 1996 */ 1997 static void 1998 long_poll_challenges_cb (void *cls, 1999 enum TALER_ErrorCode error_code, 2000 json_t *response); 2001 2002 2003 /** 2004 * Schedules the "poll" challenges action. 2005 * 2006 * @param cls NULL 2007 */ 2008 static void 2009 long_poll_challenges_task (void *cls) 2010 { 2011 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES]; 2012 json_t *tspec; 2013 2014 (void) cls; 2015 la->task = NULL; 2016 if (GNUNET_TIME_absolute_is_future (la->next_time)) 2017 { 2018 la->task = GNUNET_SCHEDULER_add_at (la->next_time, 2019 &long_poll_challenges_task, 2020 NULL); 2021 return; 2022 } 2023 la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT); 2024 tspec = GNUNET_JSON_PACK ( 2025 GNUNET_JSON_pack_time_rel ("timeout", 2026 LP_TIMEOUT)); 2027 la->ra = ANASTASIS_redux_action (AG_redux_state, 2028 "poll", 2029 tspec, 2030 &long_poll_challenges_cb, 2031 NULL); 2032 json_decref (tspec); 2033 } 2034 2035 2036 static void 2037 long_poll_challenges_cb (void *cls, 2038 enum TALER_ErrorCode error_code, 2039 json_t *response) 2040 { 2041 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES]; 2042 2043 (void) cls; 2044 la->ra = NULL; 2045 switch (error_code) 2046 { 2047 case TALER_EC_NONE: 2048 /* continued below */ 2049 break; 2050 default: 2051 AG_error (_ ("poll failed: %s (#%u)"), 2052 TALER_ErrorCode_get_hint (error_code), 2053 (unsigned int) error_code); 2054 /* simply try again */ 2055 la->task = GNUNET_SCHEDULER_add_now (&long_poll_challenges_task, 2056 NULL); 2057 return; 2058 } 2059 AG_action_cb (NULL, 2060 TALER_EC_NONE, 2061 response); 2062 } 2063 2064 2065 /** 2066 * Function called with the results of #ANASTASIS_redux_action on "sync_providers". 2067 * 2068 * @param cls NULL 2069 * @param error_code Error code 2070 * @param response new state as result or config information of a provider 2071 */ 2072 static void 2073 long_poll_sync_action_cb (void *cls, 2074 enum TALER_ErrorCode error_code, 2075 json_t *response); 2076 2077 2078 /** 2079 * Schedules the "sync_providers" action. 2080 * 2081 * @param cls NULL 2082 */ 2083 static void 2084 long_poll_sync_task (void *cls) 2085 { 2086 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS]; 2087 json_t *tspec; 2088 2089 (void) cls; 2090 la->task = NULL; 2091 if (GNUNET_TIME_absolute_is_future (la->next_time)) 2092 { 2093 la->task = GNUNET_SCHEDULER_add_at (la->next_time, 2094 &long_poll_sync_task, 2095 NULL); 2096 return; 2097 } 2098 la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT); 2099 tspec = GNUNET_JSON_PACK ( 2100 GNUNET_JSON_pack_time_rel ("timeout", 2101 LP_TIMEOUT)); 2102 la->ra = ANASTASIS_redux_action (AG_redux_state, 2103 "sync_providers", 2104 tspec, 2105 &long_poll_sync_action_cb, 2106 NULL); 2107 json_decref (tspec); 2108 } 2109 2110 2111 static void 2112 long_poll_sync_action_cb (void *cls, 2113 enum TALER_ErrorCode error_code, 2114 json_t *response) 2115 { 2116 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS]; 2117 2118 (void) cls; 2119 la->ra = NULL; 2120 switch (error_code) 2121 { 2122 case TALER_EC_NONE: 2123 /* continued below */ 2124 break; 2125 case TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID: 2126 /* we are in sync, nothing left to do */ 2127 return; 2128 default: 2129 AG_error (_ ("Synchronizing with providers failed: %s (#%u)"), 2130 TALER_ErrorCode_get_hint (error_code), 2131 (unsigned int) error_code); 2132 /* simply try again */ 2133 la->task = GNUNET_SCHEDULER_add_now (&long_poll_sync_task, 2134 NULL); 2135 return; 2136 } 2137 AG_action_cb (NULL, 2138 TALER_EC_NONE, 2139 response); 2140 } 2141 2142 2143 /** 2144 * Start policy discovery process. Triggers download(s) 2145 * of the various provider configurations and (once we 2146 * have any) starts the policy discovery process. 2147 */ 2148 static void 2149 begin_discovery (void); 2150 2151 2152 static void 2153 long_poll_providers_action_cb (void *cls, 2154 enum TALER_ErrorCode error_code, 2155 json_t *response) 2156 { 2157 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_PROVIDERS]; 2158 json_t *ap; 2159 const char *url; 2160 json_t *obj; 2161 2162 la->ra = NULL; 2163 switch (error_code) 2164 { 2165 case TALER_EC_NONE: 2166 /* continued below */ 2167 break; 2168 default: 2169 AG_error (_ ("Polling for providers failed: %s (#%u)"), 2170 TALER_ErrorCode_get_hint (error_code), 2171 (unsigned int) error_code); 2172 /* simply try again */ 2173 la->task = GNUNET_SCHEDULER_add_now (&long_poll_providers_task, 2174 NULL); 2175 return; 2176 } 2177 ap = json_object_get (response, 2178 "authentication_providers"); 2179 GNUNET_assert (NULL != ap); 2180 if (NULL == AG_pd) 2181 { 2182 json_t *ns; 2183 2184 /* Simply merge the state */ 2185 ap = json_object_get (response, 2186 "authentication_providers"); 2187 ns = json_incref (AG_redux_state); 2188 GNUNET_assert (0 == 2189 json_object_set (ns, 2190 "authentication_providers", 2191 ap)); 2192 AG_action_cb (NULL, 2193 TALER_EC_NONE, 2194 ns); 2195 json_decref (ns); 2196 return; 2197 } 2198 /* Find out which provider is new, merge that one! */ 2199 json_object_foreach (ap, url, obj) 2200 { 2201 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 2202 2203 if (GNUNET_OK == 2204 ANASTASIS_reducer_lookup_salt (AG_redux_state, 2205 url, 2206 &provider_salt)) 2207 continue; 2208 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2209 "Expanding policy discovery to recently discovered live provider `%s'\n", 2210 url); 2211 ANASTASIS_policy_discovery_more (AG_pd, 2212 url, 2213 obj); 2214 } 2215 /* update our state to the new state, but without 2216 going through the usual action loop */ 2217 json_decref (AG_redux_state); 2218 AG_redux_state = json_incref (response); 2219 GNUNET_assert (NULL != AG_redux_state); 2220 } 2221 2222 2223 static void 2224 begin_discovery (void) 2225 { 2226 GtkListStore *ls; 2227 bool have_providers; 2228 2229 ls = GTK_LIST_STORE (GCG_get_main_window_object ( 2230 "secret_selection_liststore")); 2231 GNUNET_assert (NULL != ls); 2232 if (NULL == AG_pd) 2233 gtk_list_store_clear (ls); 2234 have_providers = sync_providers (); 2235 if (NULL == AG_pd) 2236 AG_pd = ANASTASIS_policy_discovery_start (AG_redux_state, 2237 NULL, 2238 &expand_policy_list, 2239 ls); 2240 if (! have_providers) 2241 { 2242 AG_error (_ ("No available providers! Try to add one!")); 2243 } 2244 } 2245 2246 2247 /** 2248 * Function called when it is time for the user to select a secret 2249 * from the list of secrets. Builds the respective tree model. 2250 */ 2251 static void 2252 action_secret_selecting (void) 2253 { 2254 AG_hide ("anastasis_gtk_start_frame"); 2255 AG_stop_long_action (); 2256 if (AG_have_error) 2257 AG_show ("anastasis_gtk_error_label"); 2258 AG_hide ("anastasis_gtk_challenge_frame"); 2259 AG_hide ("anastasis_gtk_identity_frame"); 2260 if (NULL == AG_pd) 2261 begin_discovery (); 2262 AG_show ("anastasis_gtk_progress_vbox"); 2263 AG_progress_update (); 2264 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 2265 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 2266 AG_show ("anastasis_gtk_main_control_vbox"); 2267 AG_show ("anastasis_gtk_main_window_save_as_button"); 2268 AG_show ("anastasis_gtk_select_secret_frame"); 2269 AG_show ("anastasis_gtk_main_window_prev_button"); 2270 AG_hide ("anastasis_gtk_main_window_quit_button"); 2271 AG_insensitive ("anastasis_gtk_main_window_forward_button"); 2272 } 2273 2274 2275 /** 2276 * Callback invoked if the a "challenge"-button is clicked. 2277 * 2278 * @param object 2279 * @param user_data points to the UUID of the challenge 2280 */ 2281 static void 2282 challenge_button_clicked_cb (GObject *object, 2283 gpointer user_data) 2284 { 2285 const char *uuid = user_data; 2286 json_t *args; 2287 2288 (void) object; 2289 args = GNUNET_JSON_PACK ( 2290 GNUNET_JSON_pack_string ("uuid", 2291 uuid)); 2292 AG_freeze (); 2293 AG_ra = ANASTASIS_redux_action (AG_redux_state, 2294 "select_challenge", 2295 args, 2296 &AG_action_cb, 2297 NULL); 2298 json_decref (args); 2299 } 2300 2301 2302 /** 2303 * Generate widget about a @a challenge and add it to the 2304 * @a challenge_box. 2305 * 2306 * @param challenge_box box to expand 2307 * @param rd our recovery document (to use) 2308 * @param challenge the challenge to add 2309 * @return #GNUNET_OK on success, #GNUNET_NO 2310 * if the provider is down, #GNUNET_SYSERR on harder 2311 * internal failures 2312 */ 2313 static enum GNUNET_GenericReturnValue 2314 add_challenge (GtkBox *challenge_box, 2315 json_t *rd, 2316 json_t *uchallenge) 2317 { 2318 GtkBuilder *builder; 2319 GtkWindow *window; 2320 GtkWidget *hbox; 2321 GtkLabel *label; 2322 GtkButton *button; 2323 const char *instructions; 2324 const char *provider; 2325 const char *type; 2326 const char *uuid; 2327 struct TALER_Amount cost; 2328 bool async = false; 2329 bool solved = false; 2330 struct GNUNET_JSON_Specification uspec[] = { 2331 GNUNET_JSON_spec_string ("uuid", 2332 &uuid), 2333 GNUNET_JSON_spec_end () 2334 }; 2335 const char *iuuid; 2336 struct GNUNET_JSON_Specification spec[] = { 2337 GNUNET_JSON_spec_string ("instructions", 2338 &instructions), 2339 GNUNET_JSON_spec_string ("type", 2340 &type), 2341 GNUNET_JSON_spec_string ("url", 2342 &provider), 2343 GNUNET_JSON_spec_string ("uuid", 2344 &iuuid), 2345 GNUNET_JSON_spec_mark_optional ( 2346 GNUNET_JSON_spec_bool ("async", 2347 &async), 2348 NULL), 2349 GNUNET_JSON_spec_mark_optional ( 2350 GNUNET_JSON_spec_bool ("solved", 2351 &solved), 2352 NULL), 2353 GNUNET_JSON_spec_end () 2354 }; 2355 bool ok = true; 2356 enum GNUNET_GenericReturnValue ret; 2357 2358 if (GNUNET_OK != 2359 GNUNET_JSON_parse (uchallenge, 2360 uspec, 2361 NULL, NULL)) 2362 { 2363 GNUNET_break (0); 2364 return GNUNET_SYSERR; 2365 } 2366 { 2367 json_t *challenges; 2368 size_t index; 2369 json_t *challenge; 2370 2371 challenges = json_object_get (rd, 2372 "challenges"); 2373 /* FIXME: change data structure to have 'uuid' 2374 as the index into the 'challenges' object, instead of this 2375 'challenges' being an array */ 2376 json_array_foreach (challenges, index, challenge) 2377 { 2378 if (GNUNET_OK != 2379 GNUNET_JSON_parse (challenge, 2380 spec, 2381 NULL, NULL)) 2382 { 2383 GNUNET_break (0); 2384 return GNUNET_SYSERR; 2385 } 2386 if (0 == strcmp (uuid, 2387 iuuid)) 2388 break; 2389 } 2390 } 2391 ret = lookup_recovery_cost (provider, 2392 type, 2393 &cost); 2394 if (GNUNET_SYSERR == ret) 2395 { 2396 GNUNET_break (0); 2397 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2398 "Could not find cost of %s at %s\n", 2399 type, 2400 provider); 2401 json_dumpf (AG_redux_state, 2402 stderr, 2403 JSON_INDENT (2)); 2404 return GNUNET_SYSERR; 2405 } 2406 if (GNUNET_NO == ret) 2407 ok = false; 2408 2409 builder = ANASTASIS_GTK_get_new_builder ( 2410 ANASTASIS_GTK_project_data (), 2411 "anastasis_gtk_challenge_template.glade", 2412 NULL); 2413 window = GTK_WINDOW (gtk_builder_get_object (builder, 2414 "challenge_template_window")); 2415 hbox = GTK_WIDGET (gtk_builder_get_object (builder, 2416 "challenge_hbox")); 2417 label = GTK_LABEL (gtk_builder_get_object (builder, 2418 "challenge_label")); 2419 button = GTK_BUTTON (gtk_builder_get_object (builder, 2420 "solve_challenge_button")); 2421 if (! ok) 2422 gtk_widget_set_sensitive (GTK_WIDGET (button), 2423 false); 2424 if (solved) 2425 { 2426 GtkImage *solved_img; 2427 2428 solved_img = GTK_IMAGE (gtk_builder_get_object (builder, 2429 "challenge_solved_image")); 2430 gtk_widget_show (GTK_WIDGET (solved_img)); 2431 gtk_widget_hide (GTK_WIDGET (button)); 2432 } 2433 else 2434 { 2435 g_signal_connect (button, 2436 "clicked", 2437 G_CALLBACK (challenge_button_clicked_cb), 2438 (void *) uuid); 2439 if (ok) 2440 gtk_widget_set_tooltip_text (GTK_WIDGET (button), 2441 TALER_amount2s (&cost)); 2442 else 2443 gtk_widget_set_tooltip_text (GTK_WIDGET (button), 2444 _ ("unknown")); 2445 } 2446 { 2447 GtkImage *icon; 2448 char *lab; 2449 2450 GNUNET_asprintf (&lab, 2451 "challenge_category_%s_image", 2452 type); 2453 icon = GTK_IMAGE (gtk_builder_get_object (builder, 2454 lab)); 2455 GNUNET_free (lab); 2456 if (NULL != icon) 2457 gtk_widget_show (GTK_WIDGET (icon)); 2458 } 2459 show_challenge_feedback (uuid, 2460 builder); 2461 if (ok) 2462 gtk_label_set_text (label, 2463 instructions); 2464 else 2465 gtk_label_set_text (label, 2466 _ ("provider down")); 2467 g_object_ref (hbox); 2468 gtk_container_remove (GTK_CONTAINER (window), 2469 hbox); 2470 g_object_unref (builder); 2471 gtk_box_pack_start (challenge_box, 2472 hbox, 2473 false, 2474 false, 2475 15); 2476 if (! ok) 2477 return GNUNET_NO; 2478 return GNUNET_OK; 2479 } 2480 2481 2482 /** 2483 * Generate widget about a @a policy and add it to the 2484 * @a policy_box. 2485 * 2486 * @param policy_box box to expand 2487 * @param rd our recovery document (to use) 2488 * @param pindex policy index (for the label) 2489 * @param policy policy to add 2490 * @return true if we should long poll "sync_providers" 2491 */ 2492 static bool 2493 add_policy (GtkBox *policy_box, 2494 json_t *rd, 2495 size_t pindex, 2496 json_t *policy) 2497 { 2498 GtkBuilder *builder; 2499 GtkWindow *window; 2500 GtkWidget *widget; 2501 GtkLabel *label; 2502 GtkBox *vbox; 2503 char *txt; 2504 json_t *challenges; 2505 bool ok; 2506 2507 challenges = json_object_get (policy, 2508 "challenges"); 2509 if (NULL == challenges) 2510 { 2511 GNUNET_break_op (0); 2512 AG_error ("Policy did not parse correctly"); 2513 return false; 2514 } 2515 builder = ANASTASIS_GTK_get_new_builder ( 2516 ANASTASIS_GTK_project_data (), 2517 "anastasis_gtk_policy_template.glade", 2518 NULL); 2519 window = GTK_WINDOW (gtk_builder_get_object (builder, 2520 "policy_template_window")); 2521 widget = GTK_WIDGET (gtk_builder_get_object (builder, 2522 "policy_frame")); 2523 label = GTK_LABEL (gtk_builder_get_object (builder, 2524 "policy_label")); 2525 vbox = GTK_BOX (gtk_builder_get_object (builder, 2526 "policy_vbox")); 2527 ok = true; 2528 { 2529 size_t index; 2530 json_t *challenge; 2531 2532 json_array_foreach (challenges, index, challenge) 2533 { 2534 enum GNUNET_GenericReturnValue ret; 2535 2536 ret = add_challenge (vbox, 2537 rd, 2538 challenge); 2539 if (GNUNET_SYSERR == ret) 2540 { 2541 GNUNET_break (0); 2542 g_object_unref (builder); 2543 GNUNET_asprintf (&txt, 2544 "Failed to process challenge #%u of policy #%u", 2545 (unsigned int) (1 + index), 2546 (unsigned int) (1 + pindex)); 2547 AG_error ("%s", 2548 txt); 2549 GNUNET_free (txt); 2550 return false; 2551 } 2552 if (GNUNET_NO == ret) 2553 { 2554 gtk_widget_set_sensitive (GTK_WIDGET (vbox), 2555 false); 2556 ok = false; 2557 } 2558 } 2559 } 2560 if (ok) 2561 { 2562 GNUNET_asprintf (&txt, 2563 _ ("Policy #%u"), 2564 (unsigned int) (1 + pindex)); 2565 } 2566 else 2567 { 2568 GNUNET_asprintf (&txt, 2569 _ ("Policy #%u (unavailable, provider down)"), 2570 (unsigned int) (1 + pindex)); 2571 } 2572 gtk_label_set_text (label, 2573 txt); 2574 GNUNET_free (txt); 2575 g_object_ref (widget); 2576 gtk_container_remove (GTK_CONTAINER (window), 2577 widget); 2578 g_object_unref (builder); 2579 gtk_box_pack_start (policy_box, 2580 widget, 2581 false, 2582 false, 2583 15); 2584 return ! ok; 2585 } 2586 2587 2588 /** 2589 * The user must select the next challenge to solve 2590 * during the recovery process. 2591 */ 2592 static void 2593 action_challenge_selecting (void) 2594 { 2595 json_t *rd; 2596 json_t *policies; 2597 GtkBox *policy_box; 2598 bool sp = false; 2599 2600 AG_hide_all_frames (); 2601 AG_stop_long_action (); 2602 rd = json_object_get (AG_redux_state, 2603 "recovery_document"); 2604 policies = json_object_get (rd, 2605 "decryption_policies"); 2606 GNUNET_assert (NULL != policies); 2607 policy_box = GTK_BOX (GCG_get_main_window_object ( 2608 "anastasis_gtk_policy_vbox")); 2609 /* remove all existing entries from the box */ 2610 { 2611 GtkContainer *pb; 2612 GList *children; 2613 2614 pb = GTK_CONTAINER (policy_box); 2615 children = gtk_container_get_children (pb); 2616 for (GList *iter = children; iter != NULL; iter = g_list_next (iter)) 2617 gtk_container_remove (pb, 2618 GTK_WIDGET (iter->data)); 2619 g_list_free (children); 2620 } 2621 { 2622 size_t pindex; 2623 json_t *policy; 2624 2625 json_array_foreach (policies, pindex, policy) 2626 { 2627 sp |= add_policy (policy_box, 2628 rd, 2629 pindex, 2630 policy); 2631 } 2632 } 2633 if (sp) 2634 { 2635 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS]; 2636 2637 la->next_time = GNUNET_TIME_UNIT_ZERO_ABS; 2638 GNUNET_assert (NULL == la->task); 2639 la->task = GNUNET_SCHEDULER_add_now (&long_poll_sync_task, 2640 NULL); 2641 } 2642 2643 { 2644 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES]; 2645 json_t *challenges; 2646 size_t index; 2647 json_t *challenge; 2648 2649 challenges = json_object_get (rd, 2650 "challenges"); 2651 GNUNET_assert (NULL != challenges); 2652 json_array_foreach (challenges, index, challenge) 2653 { 2654 bool async = false; 2655 struct GNUNET_JSON_Specification spec[] = { 2656 GNUNET_JSON_spec_mark_optional ( 2657 GNUNET_JSON_spec_bool ("async", 2658 &async), 2659 NULL), 2660 GNUNET_JSON_spec_end () 2661 }; 2662 const json_t *ks; 2663 2664 ks = json_object_get (challenge, 2665 "key_share"); 2666 if ( (NULL != ks) && 2667 (! json_is_null (ks)) ) 2668 continue; /* already solved */ 2669 if (GNUNET_OK != 2670 GNUNET_JSON_parse (challenge, 2671 spec, 2672 NULL, NULL)) 2673 { 2674 GNUNET_break (0); 2675 json_dumpf (challenge, 2676 stderr, 2677 JSON_INDENT (2)); 2678 continue; 2679 } 2680 if (async && 2681 (NULL == la->task) ) 2682 { 2683 la->next_time = GNUNET_TIME_UNIT_ZERO_ABS; 2684 la->task = GNUNET_SCHEDULER_add_now (&long_poll_challenges_task, 2685 NULL); 2686 } 2687 } 2688 } 2689 2690 AG_sensitive ("anastasis_gtk_review_policy_treeview"); 2691 AG_show ("anastasis_gtk_progress_vbox"); 2692 AG_progress_update (); 2693 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 2694 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 2695 AG_show ("anastasis_gtk_main_control_vbox"); 2696 AG_show ("anastasis_gtk_main_window_save_as_button"); 2697 AG_show ("anastasis_gtk_challenge_frame"); 2698 AG_show ("anastasis_gtk_main_window_prev_button"); 2699 AG_hide ("anastasis_gtk_main_window_quit_button"); 2700 AG_hide ("anastasis_gtk_main_window_forward_button"); 2701 } 2702 2703 2704 /** 2705 * An Anastasis provider requires payment for a challenge. 2706 * Give opportunity to the user to pay. 2707 */ 2708 static void 2709 action_challenge_paying (void) 2710 { 2711 json_t *pprs; 2712 json_t *ppr; 2713 GtkListStore *ls; 2714 const char *uuid; 2715 bool found = false; 2716 const char *ps; 2717 2718 AG_hide_all_frames (); 2719 ls = GTK_LIST_STORE (GCG_get_main_window_object ( 2720 "unpaid_qrcodes_liststore")); 2721 gtk_list_store_clear (ls); 2722 pprs = json_object_get (AG_redux_state, 2723 "challenge_feedback"); 2724 json_object_foreach (pprs, uuid, ppr) 2725 { 2726 const char *state; 2727 const char *payto = NULL; 2728 const char *provider = NULL; 2729 struct GNUNET_JSON_Specification spec[] = { 2730 GNUNET_JSON_spec_string ("state", 2731 &state), 2732 GNUNET_JSON_spec_mark_optional ( 2733 GNUNET_JSON_spec_string ("taler_pay_uri", 2734 &payto), 2735 NULL), 2736 GNUNET_JSON_spec_mark_optional ( 2737 GNUNET_JSON_spec_string ("provider", 2738 &provider), 2739 NULL), 2740 GNUNET_JSON_spec_string ("payment_secret", 2741 &ps), 2742 GNUNET_JSON_spec_end () 2743 }; 2744 GdkPixbuf *pb; 2745 GtkWidget *w; 2746 2747 if (GNUNET_OK != 2748 GNUNET_JSON_parse (ppr, 2749 spec, 2750 NULL, NULL)) 2751 { 2752 GNUNET_break (0); 2753 json_dumpf (ppr, 2754 stderr, 2755 JSON_INDENT (2)); 2756 continue; 2757 } 2758 if (NULL == payto) 2759 continue; 2760 if (0 != strcmp (state, 2761 "payment")) 2762 continue; 2763 found = true; 2764 w = GTK_WIDGET (GCG_get_main_window_object ("unpaid_qr_treeview")); 2765 pb = AG_setup_qrcode (w, 2766 payto, 2767 strlen (payto)); 2768 if (NULL == pb) 2769 GNUNET_log (GNUNET_ERROR_TYPE_WARNING, 2770 _ ("Failed to initialize QR-code pixbuf for `%s'\n"), 2771 payto); 2772 gtk_list_store_insert_with_values (ls, 2773 NULL, 2774 -1, /* append */ 2775 AG_UQRMC_QR_IMAGE, pb, 2776 AG_UQRMC_URL, payto, 2777 AG_UQRMC_PROVIDER, provider, 2778 -1); 2779 g_object_unref (pb); 2780 break; 2781 } 2782 2783 if (found) 2784 { 2785 json_t *args; 2786 struct GNUNET_TIME_Relative timeout; 2787 2788 timeout = GNUNET_TIME_UNIT_MINUTES; 2789 GNUNET_assert (NULL == AG_ra); 2790 args = GNUNET_JSON_PACK ( 2791 GNUNET_JSON_pack_time_rel ("timeout", 2792 timeout), 2793 GNUNET_JSON_pack_string ("payment_secret", 2794 ps)); 2795 AG_ra = ANASTASIS_redux_action (AG_redux_state, 2796 "pay", 2797 args, 2798 &AG_action_cb, 2799 NULL); 2800 json_decref (args); 2801 } 2802 else 2803 { 2804 AG_error ("ERROR: Internal error: should pay, but do not know what"); 2805 } 2806 AG_show ("anastasis_gtk_progress_vbox"); 2807 AG_progress_update (); 2808 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 2809 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 2810 AG_show ("anastasis_gtk_pay_frame"); 2811 AG_show ("anastasis_gtk_main_control_vbox"); 2812 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 2813 AG_show ("anastasis_gtk_main_window_prev_button"); 2814 AG_hide ("anastasis_gtk_main_window_forward_button"); 2815 } 2816 2817 2818 /** 2819 * Render challenge feedback for challenge @a uuid_str in a dialog of 2820 * @a builder in the label under @a target_widget. 2821 * 2822 * Useful in case the operation previously failed at the 2823 * server and we have some useful information to return 2824 * to the user. 2825 * 2826 * FIXME: only do it AFTER the first attempt of the 2827 * user to enter a code, and/or change what the 2828 * server returns so we do NOT render a confusing 2829 * error message on first use! 2830 * 2831 * @param builder a builder to get widgets from 2832 * @param target_widget the widget to update 2833 * @param uuid_str the UUID to render feedback for 2834 */ 2835 static void 2836 render_feedback (GtkBuilder *builder, 2837 const char *target_widget, 2838 const char *uuid_str) 2839 { 2840 json_t *cf; 2841 json_t *cs; 2842 const char *state; 2843 const char *redirect_url = NULL; 2844 const char *hint = NULL; 2845 json_t *details = NULL; 2846 const char *taler_pay_uri = NULL; 2847 uint32_t ec = TALER_EC_NONE; 2848 uint32_t http_status = 0; 2849 bool hide = false; 2850 struct GNUNET_JSON_Specification spec[] = { 2851 GNUNET_JSON_spec_string ("state", 2852 &state), 2853 GNUNET_JSON_spec_mark_optional ( 2854 GNUNET_JSON_spec_string ("taler_pay_uri", 2855 &taler_pay_uri), 2856 NULL), 2857 GNUNET_JSON_spec_mark_optional ( 2858 GNUNET_JSON_spec_json ("details", 2859 &details), 2860 NULL), 2861 GNUNET_JSON_spec_mark_optional ( 2862 GNUNET_JSON_spec_string ("redirect_url", 2863 &redirect_url), 2864 NULL), 2865 GNUNET_JSON_spec_mark_optional ( 2866 GNUNET_JSON_spec_string ("hint", 2867 &hint), 2868 NULL), 2869 GNUNET_JSON_spec_mark_optional ( 2870 GNUNET_JSON_spec_uint32 ("http_status", 2871 &http_status), 2872 NULL), 2873 GNUNET_JSON_spec_mark_optional ( 2874 GNUNET_JSON_spec_uint32 ("error_code", 2875 &ec), 2876 NULL), 2877 GNUNET_JSON_spec_end () 2878 }; 2879 GtkLabel *elabel; 2880 char *msg; 2881 2882 cf = json_object_get (AG_redux_state, 2883 "challenge_feedback"); 2884 cs = json_object_get (cf, 2885 uuid_str); 2886 if (NULL == cs) 2887 return; 2888 2889 elabel = GTK_LABEL (gtk_builder_get_object (builder, 2890 target_widget)); 2891 if (NULL == elabel) 2892 { 2893 GNUNET_break (0); 2894 return; 2895 } 2896 if (GNUNET_OK != 2897 GNUNET_JSON_parse (cs, 2898 spec, 2899 NULL, NULL)) 2900 { 2901 GNUNET_break (0); 2902 gtk_label_set_text (elabel, 2903 _ ("INTERNAL ERROR: could not parse state")); 2904 gtk_widget_show (GTK_WIDGET (elabel)); 2905 return; 2906 } 2907 if ( (0 == strcmp (state, 2908 "hint")) && 2909 (NULL != hint) ) 2910 { 2911 GNUNET_asprintf (&msg, 2912 _ ("Hint (#%u): %s"), 2913 (unsigned int) http_status, 2914 dgettext ("taler-exchange", 2915 hint)); 2916 } 2917 else if ( (0 == strcmp (state, 2918 "details")) && 2919 (NULL != details) ) 2920 { 2921 uint32_t code; 2922 const char *dhint = NULL; 2923 const char *detail = NULL; 2924 struct GNUNET_JSON_Specification ispec[] = { 2925 GNUNET_JSON_spec_uint32 ("code", 2926 &code), 2927 GNUNET_JSON_spec_mark_optional ( 2928 GNUNET_JSON_spec_string ("hint", 2929 &dhint), 2930 NULL), 2931 GNUNET_JSON_spec_mark_optional ( 2932 GNUNET_JSON_spec_string ("detail", 2933 &detail), 2934 NULL), 2935 GNUNET_JSON_spec_end () 2936 }; 2937 2938 if (GNUNET_OK != 2939 GNUNET_JSON_parse (details, 2940 ispec, 2941 NULL, NULL)) 2942 { 2943 GNUNET_break (0); 2944 json_dumpf (details, 2945 stderr, 2946 JSON_INDENT (2)); 2947 msg = GNUNET_strdup ( 2948 _ ("ERROR: failed to parse server JSON instructions")); 2949 } 2950 else 2951 { 2952 const char *ihint; 2953 const char *type = "Error"; 2954 2955 switch (code) 2956 { 2957 case TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED: 2958 hide = true; 2959 type = "Hint"; 2960 break; 2961 default: 2962 break; 2963 } 2964 ihint = TALER_ErrorCode_get_hint (code); 2965 if ( (NULL != hint) && 2966 ( (NULL == ihint) || 2967 ('<' == ihint[0])) ) 2968 ihint = dhint; /* use server hint */ 2969 ihint = dgettext ("taler-exchange", 2970 ihint); 2971 if (NULL == detail) 2972 { 2973 if (NULL == ihint) 2974 GNUNET_asprintf (&msg, 2975 "%s #%u", 2976 type, 2977 (unsigned int) code); 2978 else 2979 GNUNET_asprintf (&msg, 2980 "%s #%u: %s", 2981 type, 2982 (unsigned int) code, 2983 ihint); 2984 } 2985 else 2986 { 2987 if (NULL == ihint) 2988 GNUNET_asprintf (&msg, 2989 "%s #%u (%s)", 2990 type, 2991 (unsigned int) code, 2992 detail); 2993 else 2994 GNUNET_asprintf (&msg, 2995 "%s #%u: %s (%s)", 2996 type, 2997 (unsigned int) code, 2998 ihint, 2999 detail); 3000 } 3001 } 3002 } 3003 else 3004 { 3005 GNUNET_asprintf (&msg, 3006 "ERROR: state `%s` with HTTP Status %u", 3007 state, 3008 (unsigned int) http_status); 3009 } 3010 gtk_label_set_text (elabel, 3011 msg); 3012 GNUNET_free (msg); 3013 if (hide) 3014 gtk_widget_hide (GTK_WIDGET (elabel)); 3015 else 3016 gtk_widget_show (GTK_WIDGET (elabel)); 3017 GNUNET_JSON_parse_free (spec); 3018 } 3019 3020 3021 /** 3022 * Open dialog to allow user to answer security question. 3023 * 3024 * @param details details about the challenge 3025 * @return the dialog object, or NULL on error 3026 */ 3027 static GtkDialog * 3028 diag_question (const json_t *details) 3029 { 3030 GtkBuilder *builder; 3031 GtkDialog *ad; 3032 3033 builder = ANASTASIS_GTK_get_new_builder ( 3034 ANASTASIS_GTK_project_data (), 3035 "anastasis_gtk_challenge_question.glade", 3036 NULL); 3037 if (NULL == builder) 3038 { 3039 GNUNET_break (0); 3040 return NULL; 3041 } 3042 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3043 "anastasis_gtk_c_question_dialog")); 3044 { 3045 GtkLabel *label; 3046 const char *instructions; 3047 3048 label = GTK_LABEL (gtk_builder_get_object (builder, 3049 "security_question_label")); 3050 instructions = json_string_value (json_object_get (details, 3051 "instructions")); 3052 gtk_label_set_text (label, 3053 instructions); 3054 } 3055 { 3056 const char *uuid_str; 3057 3058 uuid_str = json_string_value (json_object_get (details, 3059 "uuid-display")); 3060 render_feedback (builder, 3061 "anastasis_gtk_c_question_error_label", 3062 uuid_str); 3063 } 3064 return ad; 3065 } 3066 3067 3068 /** 3069 * Create a dialog for the user to enter a PIN code. 3070 * 3071 * @param details details about the dialog to render 3072 * @return dialog object 3073 */ 3074 static GtkDialog * 3075 diag_code (const json_t *details) 3076 { 3077 GtkBuilder *builder; 3078 const char *instructions; 3079 const char *uuid_str; 3080 struct GNUNET_JSON_Specification spec[] = { 3081 GNUNET_JSON_spec_string ("instructions", 3082 &instructions), 3083 GNUNET_JSON_spec_string ("uuid-display", 3084 &uuid_str), 3085 GNUNET_JSON_spec_end () 3086 }; 3087 3088 if (GNUNET_OK != 3089 GNUNET_JSON_parse (details, 3090 spec, 3091 NULL, NULL)) 3092 { 3093 GNUNET_break (0); 3094 json_dumpf (details, 3095 stderr, 3096 JSON_INDENT (2)); 3097 return NULL; 3098 } 3099 3100 builder = ANASTASIS_GTK_get_new_builder ( 3101 ANASTASIS_GTK_project_data (), 3102 "anastasis_gtk_challenge_code.glade", 3103 NULL); 3104 if (NULL == builder) 3105 { 3106 GNUNET_break (0); 3107 return NULL; 3108 } 3109 { 3110 GtkLabel *label; 3111 3112 label = GTK_LABEL (gtk_builder_get_object (builder, 3113 "challenge_instructions_label")); 3114 gtk_label_set_text (label, 3115 instructions); 3116 label = GTK_LABEL (gtk_builder_get_object (builder, 3117 "anastasis_gtk_c_challenge_label")); 3118 gtk_label_set_text (label, 3119 uuid_str); 3120 render_feedback (builder, 3121 "anastasis_gtk_c_code_error_label", 3122 uuid_str); 3123 } 3124 { 3125 GtkDialog *ad; 3126 3127 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3128 "anastasis_gtk_c_code_dialog")); 3129 return ad; 3130 } 3131 } 3132 3133 3134 /** 3135 * Create a dialog for the user to enter an TOTP code. 3136 * 3137 * @param details details about the dialog to render 3138 * @return dialog object 3139 */ 3140 static GtkDialog * 3141 diag_totp (const json_t *details) 3142 { 3143 GtkBuilder *builder; 3144 const char *instructions; 3145 const char *uuid_str; 3146 struct GNUNET_JSON_Specification spec[] = { 3147 GNUNET_JSON_spec_string ("instructions", 3148 &instructions), 3149 GNUNET_JSON_spec_string ("uuid-display", 3150 &uuid_str), 3151 GNUNET_JSON_spec_end () 3152 }; 3153 3154 if (GNUNET_OK != 3155 GNUNET_JSON_parse (details, 3156 spec, 3157 NULL, NULL)) 3158 { 3159 GNUNET_break (0); 3160 json_dumpf (details, 3161 stderr, 3162 JSON_INDENT (2)); 3163 return NULL; 3164 } 3165 3166 builder = ANASTASIS_GTK_get_new_builder ( 3167 ANASTASIS_GTK_project_data (), 3168 "anastasis_gtk_challenge_totp.glade", 3169 NULL); 3170 if (NULL == builder) 3171 { 3172 GNUNET_break (0); 3173 return NULL; 3174 } 3175 { 3176 GtkLabel *label; 3177 3178 label = GTK_LABEL (gtk_builder_get_object (builder, 3179 "challenge_instructions_label")); 3180 gtk_label_set_text (label, 3181 instructions); 3182 } 3183 render_feedback (builder, 3184 "anastasis_gtk_c_totp_error_label", 3185 uuid_str); 3186 { 3187 GtkDialog *ad; 3188 3189 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3190 "anastasis_gtk_c_totp_dialog")); 3191 return ad; 3192 } 3193 } 3194 3195 3196 /** 3197 * Create a dialog for the user to make an IBAN transfer. 3198 * 3199 * @param details details about the dialog to render 3200 * @return dialog object 3201 */ 3202 static GtkDialog * 3203 diag_iban (const json_t *details) 3204 { 3205 GtkBuilder *builder; 3206 struct TALER_Amount amount; 3207 const char *credit_iban; 3208 const char *business; 3209 const char *subject; 3210 const char *uuid_str; 3211 const char *debit_iban_hint; 3212 struct GNUNET_JSON_Specification spec[] = { 3213 GNUNET_JSON_spec_string ("uuid", 3214 &uuid_str), 3215 GNUNET_JSON_spec_string ("instructions", 3216 &debit_iban_hint), 3217 GNUNET_JSON_spec_end () 3218 }; 3219 3220 if (GNUNET_OK != 3221 GNUNET_JSON_parse (details, 3222 spec, 3223 NULL, NULL)) 3224 { 3225 GNUNET_break (0); 3226 json_dumpf (details, 3227 stderr, 3228 JSON_INDENT (2)); 3229 return NULL; 3230 } 3231 { 3232 json_t *cf = json_object_get (AG_redux_state, 3233 "challenge_feedback"); 3234 json_t *ci = json_object_get (cf, 3235 uuid_str); 3236 json_t *cd = json_object_get (ci, 3237 "details"); 3238 struct GNUNET_JSON_Specification ispec[] = { 3239 TALER_JSON_spec_amount_any ("challenge_amount", 3240 &amount), 3241 GNUNET_JSON_spec_string ("credit_iban", 3242 &credit_iban), 3243 GNUNET_JSON_spec_string ("business_name", 3244 &business), 3245 GNUNET_JSON_spec_string ("wire_transfer_subject", 3246 &subject), 3247 GNUNET_JSON_spec_end () 3248 }; 3249 3250 if ( (NULL == cd) || 3251 (GNUNET_OK != 3252 GNUNET_JSON_parse (cd, 3253 ispec, 3254 NULL, NULL)) ) 3255 { 3256 GNUNET_break (0); 3257 json_dumpf (AG_redux_state, 3258 stderr, 3259 JSON_INDENT (2)); 3260 return NULL; 3261 } 3262 } 3263 3264 builder = ANASTASIS_GTK_get_new_builder ( 3265 ANASTASIS_GTK_project_data (), 3266 "anastasis_gtk_challenge_iban.glade", 3267 NULL); 3268 if (NULL == builder) 3269 { 3270 GNUNET_break (0); 3271 return NULL; 3272 } 3273 { 3274 GtkLabel *label; 3275 3276 label = GTK_LABEL (gtk_builder_get_object (builder, 3277 "debit_account_label")); 3278 gtk_label_set_text (label, 3279 debit_iban_hint); 3280 label = GTK_LABEL (gtk_builder_get_object (builder, 3281 "credit_account_label")); 3282 gtk_label_set_text (label, 3283 credit_iban); 3284 label = GTK_LABEL (gtk_builder_get_object (builder, 3285 "provider_name_label")); 3286 gtk_label_set_text (label, 3287 business); 3288 label = GTK_LABEL (gtk_builder_get_object (builder, 3289 "wire_transfer_subject_label")); 3290 gtk_label_set_text (label, 3291 subject); 3292 label = GTK_LABEL (gtk_builder_get_object (builder, 3293 "amount_label")); 3294 gtk_label_set_text (label, 3295 TALER_amount2s (&amount)); 3296 } 3297 3298 { 3299 GtkDialog *ad; 3300 3301 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3302 "anastasis_gtk_c_iban_dialog")); 3303 return ad; 3304 } 3305 } 3306 3307 3308 /** 3309 * The user wants to solve the selected challenge. Launch the 3310 * dialog to allow the user to enter the solution. 3311 */ 3312 static void 3313 action_challenge_solving (void) 3314 { 3315 struct 3316 { 3317 const char *type; 3318 GtkDialog *(*ctor)(const json_t *details); 3319 } type_map [] = { 3320 { .type = gettext_noop ("question"), 3321 .ctor = &diag_question }, 3322 { .type = gettext_noop ("sms"), 3323 .ctor = &diag_code }, 3324 { .type = gettext_noop ("post"), 3325 .ctor = &diag_code }, 3326 { .type = gettext_noop ("email"), 3327 .ctor = &diag_code }, 3328 { .type = gettext_noop ("iban"), 3329 .ctor = &diag_iban }, 3330 { .type = gettext_noop ("totp"), 3331 .ctor = &diag_totp }, 3332 { .type = NULL, 3333 .ctor = NULL } 3334 }; 3335 const char *type; 3336 GtkDialog *diag; 3337 const char *uuid; 3338 const json_t *challenge; 3339 3340 uuid = json_string_value (json_object_get (AG_redux_state, 3341 "selected_challenge_uuid")); 3342 if (NULL == uuid) 3343 { 3344 GNUNET_break (0); 3345 return; 3346 } 3347 challenge = find_challenge_by_uuid (uuid); 3348 if (NULL == challenge) 3349 { 3350 GNUNET_break (0); 3351 return; 3352 } 3353 type = json_string_value (json_object_get (challenge, 3354 "type")); 3355 if (NULL == type) 3356 { 3357 GNUNET_break (0); 3358 return; 3359 } 3360 /* create dialog based on challenge type */ 3361 diag = NULL; 3362 for (unsigned int i = 0; NULL != type_map[i].type; i++) 3363 { 3364 if (0 != strcmp (type_map[i].type, 3365 type)) 3366 continue; 3367 diag = type_map[i].ctor (challenge); 3368 break; 3369 } 3370 if (NULL == diag) 3371 { 3372 GNUNET_break (0); 3373 return; 3374 } 3375 /* show dialog */ 3376 { 3377 GtkWidget *toplevel; 3378 GtkWidget *box; 3379 3380 box = GTK_WIDGET (GCG_get_main_window_object ( 3381 "anastasis_gtk_policy_vbox")); 3382 toplevel = gtk_widget_get_toplevel (box); 3383 gtk_window_set_transient_for (GTK_WINDOW (diag), 3384 GTK_WINDOW (toplevel)); 3385 gtk_window_present (GTK_WINDOW (diag)); 3386 } 3387 } 3388 3389 3390 /** 3391 * The recovery process was finished. Show the recovered secret to the 3392 * user. 3393 */ 3394 static void 3395 action_recovery_finished (void) 3396 { 3397 const char *mime = NULL; 3398 const char *text = NULL; 3399 const char *name = NULL; 3400 void *data = NULL; 3401 size_t data_size = 0; 3402 const json_t *cs; 3403 struct GNUNET_JSON_Specification spec[] = { 3404 GNUNET_JSON_spec_mark_optional ( 3405 GNUNET_JSON_spec_string ("mime", 3406 &mime), 3407 NULL), 3408 GNUNET_JSON_spec_mark_optional ( 3409 GNUNET_JSON_spec_string ("text", 3410 &text), 3411 NULL), 3412 GNUNET_JSON_spec_mark_optional ( 3413 GNUNET_JSON_spec_varsize ("value", 3414 &data, 3415 &data_size), 3416 NULL), 3417 GNUNET_JSON_spec_end () 3418 }; 3419 GdkPixbuf *pb; 3420 GtkImage *img; 3421 3422 AG_hide_all_frames (); 3423 name = json_string_value (json_object_get (json_object_get (AG_redux_state, 3424 "recovery_information"), 3425 "secret_name")); 3426 3427 cs = json_object_get (AG_redux_state, 3428 "core_secret"); 3429 GNUNET_assert (NULL != cs); 3430 GNUNET_assert (GNUNET_OK == 3431 GNUNET_JSON_parse (cs, 3432 spec, 3433 NULL, NULL)); 3434 AG_hide ("anastasis_gtk_secret_copy_button"); 3435 update_label ("anastasis_gtk_secret_value_label", 3436 text); 3437 if ( (NULL != name) && 3438 (0 != strlen (name)) ) 3439 update_label ("recovery_secret_name_value_label", 3440 name); 3441 else 3442 update_label ("recovery_secret_name_value_label", 3443 _ ("You did not name this secret")); 3444 if ( (0 == strncasecmp (mime, 3445 "text/", 3446 strlen ("text/"))) || 3447 (0 == strncasecmp (mime, 3448 "image/", 3449 strlen ("image/"))) || 3450 (NULL != text) ) 3451 { 3452 /* images and text can be copied */ 3453 AG_show ("anastasis_gtk_secret_copy_button"); 3454 } 3455 pb = NULL; 3456 img = GTK_IMAGE (GCG_get_main_window_object ( 3457 "anastasis_gtk_secret_qr_image")); 3458 if (NULL != text) 3459 { 3460 pb = AG_setup_qrcode (GTK_WIDGET (img), 3461 text, 3462 strlen (text)); 3463 } 3464 else 3465 { 3466 pb = AG_setup_qrcode (GTK_WIDGET (img), 3467 data, 3468 data_size); 3469 } 3470 if (NULL != pb) 3471 { 3472 gtk_image_set_from_pixbuf (img, 3473 pb); 3474 g_object_unref (pb); 3475 } 3476 else 3477 { 3478 AG_hide ("anastasis_gtk_secret_qr_image"); 3479 } 3480 GNUNET_JSON_parse_free (spec); 3481 AG_hide ("anastasis_gtk_progress_vbox"); 3482 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 3483 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 3484 AG_show ("anastasis_gtk_completed_frame"); 3485 AG_hide ("anastasis_gtk_backup_complete_box"); 3486 AG_hide ("anastasis_gtk_success_backup_label"); 3487 AG_show ("anastasis_gtk_success_recovery_box"); 3488 AG_show ("anastasis_gtk_main_control_vbox"); 3489 AG_hide ("anastasis_gtk_main_window_save_as_button"); 3490 AG_show ("anastasis_gtk_restart_button"); 3491 AG_show ("anastasis_gtk_main_window_quit_button"); 3492 AG_hide ("anastasis_gtk_main_window_prev_button"); 3493 AG_hide ("anastasis_gtk_main_window_forward_button"); 3494 } 3495 3496 3497 /** 3498 * Function called with the results of #ANASTASIS_redux_action. 3499 * 3500 * @param cls closure 3501 * @param error_code Error code 3502 * @param response new state as result or config information of a provider 3503 */ 3504 void 3505 AG_action_cb (void *cls, 3506 enum TALER_ErrorCode error_code, 3507 json_t *response) 3508 { 3509 struct DispatchItem actions[] = { 3510 { .state = "CONTINENT_SELECTING", 3511 .action = &action_continent_selecting }, 3512 { .state = "COUNTRY_SELECTING", 3513 .action = &action_country_selecting }, 3514 { .state = "USER_ATTRIBUTES_COLLECTING", 3515 .action = &action_user_attributes_collecting }, 3516 { .state = "AUTHENTICATIONS_EDITING", 3517 .action = &action_authentications_editing }, 3518 { .state = "POLICIES_REVIEWING", 3519 .action = &action_policies_reviewing }, 3520 { .state = "SECRET_EDITING", 3521 .action = &action_secret_editing }, 3522 { .state = "TRUTHS_PAYING", 3523 .action = &action_truths_paying }, 3524 { .state = "POLICIES_PAYING", 3525 .action = &action_policies_paying }, 3526 { .state = "BACKUP_FINISHED", 3527 .action = &action_backup_finished }, 3528 { .state = "SECRET_SELECTING", 3529 .action = &action_secret_selecting }, 3530 { .state = "CHALLENGE_SELECTING", 3531 .action = &action_challenge_selecting }, 3532 { .state = "CHALLENGE_PAYING", 3533 .action = &action_challenge_paying }, 3534 { .state = "CHALLENGE_SOLVING", 3535 .action = &action_challenge_solving }, 3536 { .state = "RECOVERY_FINISHED", 3537 .action = &action_recovery_finished }, 3538 { .state = NULL, 3539 .action = NULL } 3540 }; 3541 3542 (void) cls; 3543 AG_ra = NULL; 3544 AG_thaw (); 3545 #if DEBUG 3546 fprintf (stderr, 3547 "Action result %d\n", 3548 error_code); 3549 json_dumpf (response, 3550 stderr, 3551 JSON_INDENT (2)); 3552 fprintf (stderr, 3553 "END action result %d\n", 3554 error_code); 3555 #endif 3556 if (TALER_EC_NONE != error_code) 3557 { 3558 /* FIXME: maybe also render 'detail' 3559 if present in state? 3560 See for example ~ anastasis_api_backup_redux.c:3082 */ 3561 AG_error ("Error #%d: %s\n", 3562 (int) error_code, 3563 TALER_ErrorCode_get_hint (error_code)); 3564 if (AG_in_action) 3565 { 3566 GNUNET_break (0); 3567 return; 3568 } 3569 } 3570 if ( (NULL != json_object_get (response, 3571 "backup_state")) || 3572 (NULL != json_object_get (response, 3573 "recovery_state")) ) 3574 { 3575 json_decref (AG_redux_state); 3576 AG_stop_long_action (); 3577 AG_redux_state = json_incref (response); 3578 } 3579 if ( (TALER_EC_ANASTASIS_TRUTH_UNKNOWN == error_code) || 3580 (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED == error_code) ) 3581 { 3582 /* special case: do not remain in previous (challenge selected) 3583 state but revert to challenge selecting */ 3584 GNUNET_assert (0 == 3585 json_object_set_new (AG_redux_state, 3586 "recovery_state", 3587 json_string ("CHALLENGE_SELECTING"))); 3588 } 3589 if ( (TALER_EC_ANASTASIS_REDUCER_NETWORK_FAILED == error_code) || 3590 (TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED == error_code) || 3591 (TALER_EC_ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED == error_code) ) 3592 { 3593 /* special case: do not remain in previous (enter identity) 3594 state but advance to secret selecting */ 3595 GNUNET_assert (0 == 3596 json_object_set_new (AG_redux_state, 3597 "recovery_state", 3598 json_string ("SECRET_SELECTING"))); 3599 } 3600 AG_in_action = true; 3601 if (GNUNET_OK == 3602 AG_dispatch (actions)) 3603 { 3604 AG_in_action = false; 3605 return; 3606 } 3607 AG_in_action = false; 3608 AG_error ("Unhandled state `%s/%s'", 3609 json_string_value (json_object_get (AG_redux_state, 3610 "backup_state")), 3611 json_string_value (json_object_get (AG_redux_state, 3612 "recovery_state"))); 3613 json_dumpf (AG_redux_state, 3614 stderr, 3615 JSON_INDENT (2)); 3616 json_decref (AG_redux_state); 3617 AG_redux_state = NULL; 3618 AG_hide_all_frames (); 3619 AG_show ("anastasis_gtk_start_frame"); 3620 }