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