anastasis-gtk_action.c (105290B)
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_string ("secret_name", 1951 secret_name), 1952 GNUNET_JSON_pack_string ("date_string", 1953 GNUNET_TIME_timestamp2s (server_time)), 1954 GNUNET_JSON_pack_uint64 ("date", 1955 server_time.abs_time.abs_value_us), 1956 GNUNET_JSON_pack_array_incref ("providers", 1957 (json_t *) providers))); 1958 if (! have_first) 1959 AG_select_row ("anastasis_gtk_secret_selection_treeselection", 1960 0); 1961 } 1962 1963 1964 /** 1965 * Function called with the results of #ANASTASIS_redux_action on "poll". 1966 * 1967 * @param cls NULL 1968 * @param error_code Error code 1969 * @param response new state as result or config information of a provider 1970 */ 1971 static void 1972 long_poll_challenges_cb (void *cls, 1973 enum TALER_ErrorCode error_code, 1974 json_t *response); 1975 1976 1977 /** 1978 * Schedules the "poll" challenges action. 1979 * 1980 * @param cls NULL 1981 */ 1982 static void 1983 long_poll_challenges_task (void *cls) 1984 { 1985 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES]; 1986 json_t *tspec; 1987 1988 (void) cls; 1989 la->task = NULL; 1990 if (GNUNET_TIME_absolute_is_future (la->next_time)) 1991 { 1992 la->task = GNUNET_SCHEDULER_add_at (la->next_time, 1993 &long_poll_challenges_task, 1994 NULL); 1995 return; 1996 } 1997 la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT); 1998 tspec = GNUNET_JSON_PACK ( 1999 GNUNET_JSON_pack_time_rel ("timeout", 2000 LP_TIMEOUT)); 2001 la->ra = ANASTASIS_redux_action (AG_redux_state, 2002 "poll", 2003 tspec, 2004 &long_poll_challenges_cb, 2005 NULL); 2006 json_decref (tspec); 2007 } 2008 2009 2010 static void 2011 long_poll_challenges_cb (void *cls, 2012 enum TALER_ErrorCode error_code, 2013 json_t *response) 2014 { 2015 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES]; 2016 2017 (void) cls; 2018 la->ra = NULL; 2019 switch (error_code) 2020 { 2021 case TALER_EC_NONE: 2022 /* continued below */ 2023 break; 2024 default: 2025 AG_error (_ ("poll failed: %s (#%u)"), 2026 TALER_ErrorCode_get_hint (error_code), 2027 (unsigned int) error_code); 2028 /* simply try again */ 2029 la->task = GNUNET_SCHEDULER_add_now (&long_poll_challenges_task, 2030 NULL); 2031 return; 2032 } 2033 AG_action_cb (NULL, 2034 TALER_EC_NONE, 2035 response); 2036 } 2037 2038 2039 /** 2040 * Function called with the results of #ANASTASIS_redux_action on "sync_providers". 2041 * 2042 * @param cls NULL 2043 * @param error_code Error code 2044 * @param response new state as result or config information of a provider 2045 */ 2046 static void 2047 long_poll_sync_action_cb (void *cls, 2048 enum TALER_ErrorCode error_code, 2049 json_t *response); 2050 2051 2052 /** 2053 * Schedules the "sync_providers" action. 2054 * 2055 * @param cls NULL 2056 */ 2057 static void 2058 long_poll_sync_task (void *cls) 2059 { 2060 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS]; 2061 json_t *tspec; 2062 2063 (void) cls; 2064 la->task = NULL; 2065 if (GNUNET_TIME_absolute_is_future (la->next_time)) 2066 { 2067 la->task = GNUNET_SCHEDULER_add_at (la->next_time, 2068 &long_poll_sync_task, 2069 NULL); 2070 return; 2071 } 2072 la->next_time = GNUNET_TIME_relative_to_absolute (LP_TIMEOUT); 2073 tspec = GNUNET_JSON_PACK ( 2074 GNUNET_JSON_pack_time_rel ("timeout", 2075 LP_TIMEOUT)); 2076 la->ra = ANASTASIS_redux_action (AG_redux_state, 2077 "sync_providers", 2078 tspec, 2079 &long_poll_sync_action_cb, 2080 NULL); 2081 json_decref (tspec); 2082 } 2083 2084 2085 static void 2086 long_poll_sync_action_cb (void *cls, 2087 enum TALER_ErrorCode error_code, 2088 json_t *response) 2089 { 2090 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS]; 2091 2092 (void) cls; 2093 la->ra = NULL; 2094 switch (error_code) 2095 { 2096 case TALER_EC_NONE: 2097 /* continued below */ 2098 break; 2099 case TALER_EC_ANASTASIS_REDUCER_ACTION_INVALID: 2100 /* we are in sync, nothing left to do */ 2101 return; 2102 default: 2103 AG_error (_ ("Synchronizing with providers failed: %s (#%u)"), 2104 TALER_ErrorCode_get_hint (error_code), 2105 (unsigned int) error_code); 2106 /* simply try again */ 2107 la->task = GNUNET_SCHEDULER_add_now (&long_poll_sync_task, 2108 NULL); 2109 return; 2110 } 2111 AG_action_cb (NULL, 2112 TALER_EC_NONE, 2113 response); 2114 } 2115 2116 2117 /** 2118 * Start policy discovery process. Triggers download(s) 2119 * of the various provider configurations and (once we 2120 * have any) starts the policy discovery process. 2121 */ 2122 static void 2123 begin_discovery (void); 2124 2125 2126 static void 2127 long_poll_providers_action_cb (void *cls, 2128 enum TALER_ErrorCode error_code, 2129 json_t *response) 2130 { 2131 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_PROVIDERS]; 2132 json_t *ap; 2133 const char *url; 2134 json_t *obj; 2135 2136 la->ra = NULL; 2137 switch (error_code) 2138 { 2139 case TALER_EC_NONE: 2140 /* continued below */ 2141 break; 2142 default: 2143 AG_error (_ ("Polling for providers failed: %s (#%u)"), 2144 TALER_ErrorCode_get_hint (error_code), 2145 (unsigned int) error_code); 2146 /* simply try again */ 2147 la->task = GNUNET_SCHEDULER_add_now (&long_poll_providers_task, 2148 NULL); 2149 return; 2150 } 2151 ap = json_object_get (response, 2152 "authentication_providers"); 2153 GNUNET_assert (NULL != ap); 2154 if (NULL == AG_pd) 2155 { 2156 json_t *ns; 2157 2158 /* Simply merge the state */ 2159 ap = json_object_get (response, 2160 "authentication_providers"); 2161 ns = json_incref (AG_redux_state); 2162 GNUNET_assert (0 == 2163 json_object_set (ns, 2164 "authentication_providers", 2165 ap)); 2166 AG_action_cb (NULL, 2167 TALER_EC_NONE, 2168 ns); 2169 json_decref (ns); 2170 return; 2171 } 2172 /* Find out which provider is new, merge that one! */ 2173 json_object_foreach (ap, url, obj) 2174 { 2175 struct ANASTASIS_CRYPTO_ProviderSaltP provider_salt; 2176 2177 if (GNUNET_OK == 2178 ANASTASIS_reducer_lookup_salt (AG_redux_state, 2179 url, 2180 &provider_salt)) 2181 continue; 2182 GNUNET_log (GNUNET_ERROR_TYPE_INFO, 2183 "Expanding policy discovery to recently discovered live provider `%s'\n", 2184 url); 2185 ANASTASIS_policy_discovery_more (AG_pd, 2186 url, 2187 obj); 2188 } 2189 /* update our state to the new state, but without 2190 going through the usual action loop */ 2191 json_decref (AG_redux_state); 2192 AG_redux_state = json_incref (response); 2193 GNUNET_assert (NULL != AG_redux_state); 2194 } 2195 2196 2197 static void 2198 begin_discovery (void) 2199 { 2200 GListStore *ls; 2201 bool have_providers; 2202 2203 ls = AG_list ("secret_selection_liststore"); 2204 GNUNET_assert (NULL != ls); 2205 if (NULL == AG_pd) 2206 g_list_store_remove_all (ls); 2207 have_providers = sync_providers (); 2208 if (NULL == AG_pd) 2209 AG_pd = ANASTASIS_policy_discovery_start (AG_redux_state, 2210 NULL, 2211 &expand_policy_list, 2212 ls); 2213 if (! have_providers) 2214 { 2215 AG_error (_ ("No available providers! Try to add one!")); 2216 } 2217 } 2218 2219 2220 /** 2221 * Function called when it is time for the user to select a secret 2222 * from the list of secrets. Builds the respective tree model. 2223 */ 2224 static void 2225 action_secret_selecting (void) 2226 { 2227 AG_hide ("anastasis_gtk_start_frame"); 2228 AG_stop_long_action (); 2229 if (AG_have_error) 2230 AG_show ("anastasis_gtk_error_label"); 2231 AG_hide ("anastasis_gtk_challenge_frame"); 2232 AG_hide ("anastasis_gtk_identity_frame"); 2233 if (NULL == AG_pd) 2234 begin_discovery (); 2235 AG_show ("anastasis_gtk_progress_vbox"); 2236 AG_progress_update (); 2237 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 2238 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 2239 AG_show ("anastasis_gtk_main_control_vbox"); 2240 AG_show ("anastasis_gtk_main_window_save_as_button"); 2241 AG_show ("anastasis_gtk_select_secret_frame"); 2242 AG_show ("anastasis_gtk_main_window_prev_button"); 2243 AG_hide ("anastasis_gtk_main_window_quit_button"); 2244 AG_insensitive ("anastasis_gtk_main_window_forward_button"); 2245 } 2246 2247 2248 /** 2249 * Callback invoked if the a "challenge"-button is clicked. 2250 * 2251 * @param object 2252 * @param user_data points to the UUID of the challenge 2253 */ 2254 static void 2255 challenge_button_clicked_cb (GObject *object, 2256 gpointer user_data) 2257 { 2258 const char *uuid = user_data; 2259 json_t *args; 2260 2261 (void) object; 2262 args = GNUNET_JSON_PACK ( 2263 GNUNET_JSON_pack_string ("uuid", 2264 uuid)); 2265 AG_freeze (); 2266 AG_ra = ANASTASIS_redux_action (AG_redux_state, 2267 "select_challenge", 2268 args, 2269 &AG_action_cb, 2270 NULL); 2271 json_decref (args); 2272 } 2273 2274 2275 /** 2276 * Generate widget about a @a challenge and add it to the 2277 * @a challenge_box. 2278 * 2279 * @param challenge_box box to expand 2280 * @param rd our recovery document (to use) 2281 * @param challenge the challenge to add 2282 * @return #GNUNET_OK on success, #GNUNET_NO 2283 * if the provider is down, #GNUNET_SYSERR on harder 2284 * internal failures 2285 */ 2286 static enum GNUNET_GenericReturnValue 2287 add_challenge (GtkBox *challenge_box, 2288 json_t *rd, 2289 json_t *uchallenge) 2290 { 2291 GtkBuilder *builder; 2292 GtkWindow *window; 2293 GtkWidget *hbox; 2294 GtkLabel *label; 2295 GtkButton *button; 2296 const char *instructions; 2297 const char *provider; 2298 const char *type; 2299 const char *uuid; 2300 struct TALER_Amount cost; 2301 bool async = false; 2302 bool solved = false; 2303 struct GNUNET_JSON_Specification uspec[] = { 2304 GNUNET_JSON_spec_string ("uuid", 2305 &uuid), 2306 GNUNET_JSON_spec_end () 2307 }; 2308 const char *iuuid; 2309 struct GNUNET_JSON_Specification spec[] = { 2310 GNUNET_JSON_spec_string ("instructions", 2311 &instructions), 2312 GNUNET_JSON_spec_string ("type", 2313 &type), 2314 GNUNET_JSON_spec_string ("url", 2315 &provider), 2316 GNUNET_JSON_spec_string ("uuid", 2317 &iuuid), 2318 GNUNET_JSON_spec_mark_optional ( 2319 GNUNET_JSON_spec_bool ("async", 2320 &async), 2321 NULL), 2322 GNUNET_JSON_spec_mark_optional ( 2323 GNUNET_JSON_spec_bool ("solved", 2324 &solved), 2325 NULL), 2326 GNUNET_JSON_spec_end () 2327 }; 2328 bool ok = true; 2329 enum GNUNET_GenericReturnValue ret; 2330 2331 if (GNUNET_OK != 2332 GNUNET_JSON_parse (uchallenge, 2333 uspec, 2334 NULL, NULL)) 2335 { 2336 GNUNET_break (0); 2337 return GNUNET_SYSERR; 2338 } 2339 { 2340 json_t *challenges; 2341 size_t index; 2342 json_t *challenge; 2343 2344 challenges = json_object_get (rd, 2345 "challenges"); 2346 /* FIXME: change data structure to have 'uuid' 2347 as the index into the 'challenges' object, instead of this 2348 'challenges' being an array */ 2349 json_array_foreach (challenges, index, challenge) 2350 { 2351 if (GNUNET_OK != 2352 GNUNET_JSON_parse (challenge, 2353 spec, 2354 NULL, NULL)) 2355 { 2356 GNUNET_break (0); 2357 return GNUNET_SYSERR; 2358 } 2359 if (0 == strcmp (uuid, 2360 iuuid)) 2361 break; 2362 } 2363 } 2364 ret = lookup_recovery_cost (provider, 2365 type, 2366 &cost); 2367 if (GNUNET_SYSERR == ret) 2368 { 2369 GNUNET_break (0); 2370 GNUNET_log (GNUNET_ERROR_TYPE_ERROR, 2371 "Could not find cost of %s at %s\n", 2372 type, 2373 provider); 2374 json_dumpf (AG_redux_state, 2375 stderr, 2376 JSON_INDENT (2)); 2377 return GNUNET_SYSERR; 2378 } 2379 if (GNUNET_NO == ret) 2380 ok = false; 2381 2382 builder = ANASTASIS_GTK_get_new_builder ( 2383 ANASTASIS_GTK_project_data (), 2384 "anastasis_gtk_challenge_template.ui", 2385 NULL); 2386 window = GTK_WINDOW (gtk_builder_get_object (builder, 2387 "challenge_template_window")); 2388 hbox = GTK_WIDGET (gtk_builder_get_object (builder, 2389 "challenge_hbox")); 2390 label = GTK_LABEL (gtk_builder_get_object (builder, 2391 "challenge_label")); 2392 button = GTK_BUTTON (gtk_builder_get_object (builder, 2393 "solve_challenge_button")); 2394 if (! ok) 2395 gtk_widget_set_sensitive (GTK_WIDGET (button), 2396 false); 2397 if (solved) 2398 { 2399 GtkWidget *solved_img; 2400 2401 solved_img = GTK_WIDGET (gtk_builder_get_object (builder, 2402 "challenge_solved_image")); 2403 gtk_widget_set_visible (solved_img, 2404 TRUE); 2405 gtk_widget_set_visible (GTK_WIDGET (button), 2406 FALSE); 2407 } 2408 else 2409 { 2410 g_signal_connect (button, 2411 "clicked", 2412 G_CALLBACK (challenge_button_clicked_cb), 2413 (void *) uuid); 2414 if (ok) 2415 gtk_widget_set_tooltip_text (GTK_WIDGET (button), 2416 TALER_amount2s (&cost)); 2417 else 2418 gtk_widget_set_tooltip_text (GTK_WIDGET (button), 2419 _ ("unknown")); 2420 } 2421 { 2422 GtkWidget *icon; 2423 char *lab; 2424 2425 GNUNET_asprintf (&lab, 2426 "challenge_category_%s_image", 2427 type); 2428 icon = GTK_WIDGET (gtk_builder_get_object (builder, 2429 lab)); 2430 GNUNET_free (lab); 2431 if (NULL != icon) 2432 gtk_widget_set_visible (icon, 2433 TRUE); 2434 } 2435 show_challenge_feedback (uuid, 2436 builder); 2437 if (ok) 2438 gtk_label_set_text (label, 2439 instructions); 2440 else 2441 gtk_label_set_text (label, 2442 _ ("provider down")); 2443 g_object_ref (hbox); 2444 gtk_window_set_child (window, 2445 NULL); 2446 g_object_unref (builder); 2447 pad_along (GTK_ORIENTATION_VERTICAL, 2448 hbox, 2449 15); 2450 gtk_box_append (challenge_box, 2451 hbox); 2452 g_object_unref (hbox); 2453 if (! ok) 2454 return GNUNET_NO; 2455 return GNUNET_OK; 2456 } 2457 2458 2459 /** 2460 * Generate widget about a @a policy and add it to the 2461 * @a policy_box. 2462 * 2463 * @param policy_box box to expand 2464 * @param rd our recovery document (to use) 2465 * @param pindex policy index (for the label) 2466 * @param policy policy to add 2467 * @return true if we should long poll "sync_providers" 2468 */ 2469 static bool 2470 add_policy (GtkBox *policy_box, 2471 json_t *rd, 2472 size_t pindex, 2473 json_t *policy) 2474 { 2475 GtkBuilder *builder; 2476 GtkWindow *window; 2477 GtkWidget *widget; 2478 GtkLabel *label; 2479 GtkBox *vbox; 2480 char *txt; 2481 json_t *challenges; 2482 bool ok; 2483 2484 challenges = json_object_get (policy, 2485 "challenges"); 2486 if (NULL == challenges) 2487 { 2488 GNUNET_break_op (0); 2489 AG_error ("Policy did not parse correctly"); 2490 return false; 2491 } 2492 builder = ANASTASIS_GTK_get_new_builder ( 2493 ANASTASIS_GTK_project_data (), 2494 "anastasis_gtk_policy_template.ui", 2495 NULL); 2496 window = GTK_WINDOW (gtk_builder_get_object (builder, 2497 "policy_template_window")); 2498 widget = GTK_WIDGET (gtk_builder_get_object (builder, 2499 "policy_frame")); 2500 label = GTK_LABEL (gtk_builder_get_object (builder, 2501 "policy_label")); 2502 vbox = GTK_BOX (gtk_builder_get_object (builder, 2503 "policy_vbox")); 2504 ok = true; 2505 { 2506 size_t index; 2507 json_t *challenge; 2508 2509 json_array_foreach (challenges, index, challenge) 2510 { 2511 enum GNUNET_GenericReturnValue ret; 2512 2513 ret = add_challenge (vbox, 2514 rd, 2515 challenge); 2516 if (GNUNET_SYSERR == ret) 2517 { 2518 GNUNET_break (0); 2519 g_object_unref (builder); 2520 GNUNET_asprintf (&txt, 2521 "Failed to process challenge #%u of policy #%u", 2522 (unsigned int) (1 + index), 2523 (unsigned int) (1 + pindex)); 2524 AG_error ("%s", 2525 txt); 2526 GNUNET_free (txt); 2527 return false; 2528 } 2529 if (GNUNET_NO == ret) 2530 { 2531 gtk_widget_set_sensitive (GTK_WIDGET (vbox), 2532 false); 2533 ok = false; 2534 } 2535 } 2536 } 2537 if (ok) 2538 { 2539 GNUNET_asprintf (&txt, 2540 _ ("Policy #%u"), 2541 (unsigned int) (1 + pindex)); 2542 } 2543 else 2544 { 2545 GNUNET_asprintf (&txt, 2546 _ ("Policy #%u (unavailable, provider down)"), 2547 (unsigned int) (1 + pindex)); 2548 } 2549 gtk_label_set_text (label, 2550 txt); 2551 GNUNET_free (txt); 2552 g_object_ref (widget); 2553 gtk_window_set_child (window, 2554 NULL); 2555 g_object_unref (builder); 2556 pad_along (GTK_ORIENTATION_VERTICAL, 2557 widget, 2558 15); 2559 gtk_box_append (policy_box, 2560 widget); 2561 g_object_unref (widget); 2562 return ! ok; 2563 } 2564 2565 2566 /** 2567 * The user must select the next challenge to solve 2568 * during the recovery process. 2569 */ 2570 static void 2571 action_challenge_selecting (void) 2572 { 2573 json_t *rd; 2574 json_t *policies; 2575 GtkBox *policy_box; 2576 bool sp = false; 2577 2578 AG_hide_all_frames (); 2579 AG_stop_long_action (); 2580 rd = json_object_get (AG_redux_state, 2581 "recovery_document"); 2582 policies = json_object_get (rd, 2583 "decryption_policies"); 2584 GNUNET_assert (NULL != policies); 2585 policy_box = GTK_BOX (GCG_get_main_window_object ( 2586 "anastasis_gtk_policy_vbox")); 2587 /* remove all existing entries from the box */ 2588 { 2589 GtkWidget *child; 2590 2591 while (NULL != (child = gtk_widget_get_first_child ( 2592 GTK_WIDGET (policy_box)))) 2593 gtk_box_remove (policy_box, 2594 child); 2595 } 2596 { 2597 size_t pindex; 2598 json_t *policy; 2599 2600 json_array_foreach (policies, pindex, policy) 2601 { 2602 sp |= add_policy (policy_box, 2603 rd, 2604 pindex, 2605 policy); 2606 } 2607 } 2608 if (sp) 2609 { 2610 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_SYNC_PROVIDERS]; 2611 2612 la->next_time = GNUNET_TIME_UNIT_ZERO_ABS; 2613 GNUNET_assert (NULL == la->task); 2614 la->task = GNUNET_SCHEDULER_add_now (&long_poll_sync_task, 2615 NULL); 2616 } 2617 2618 { 2619 struct ANASTASIS_LongAction *la = &AG_lacs[ANASTASIS_LP_POLL_CHALLENGES]; 2620 json_t *challenges; 2621 size_t index; 2622 json_t *challenge; 2623 2624 challenges = json_object_get (rd, 2625 "challenges"); 2626 GNUNET_assert (NULL != challenges); 2627 json_array_foreach (challenges, index, challenge) 2628 { 2629 bool async = false; 2630 struct GNUNET_JSON_Specification spec[] = { 2631 GNUNET_JSON_spec_mark_optional ( 2632 GNUNET_JSON_spec_bool ("async", 2633 &async), 2634 NULL), 2635 GNUNET_JSON_spec_end () 2636 }; 2637 const json_t *ks; 2638 2639 ks = json_object_get (challenge, 2640 "key_share"); 2641 if ( (NULL != ks) && 2642 (! json_is_null (ks)) ) 2643 continue; /* already solved */ 2644 if (GNUNET_OK != 2645 GNUNET_JSON_parse (challenge, 2646 spec, 2647 NULL, NULL)) 2648 { 2649 GNUNET_break (0); 2650 json_dumpf (challenge, 2651 stderr, 2652 JSON_INDENT (2)); 2653 continue; 2654 } 2655 if (async && 2656 (NULL == la->task) ) 2657 { 2658 la->next_time = GNUNET_TIME_UNIT_ZERO_ABS; 2659 la->task = GNUNET_SCHEDULER_add_now (&long_poll_challenges_task, 2660 NULL); 2661 } 2662 } 2663 } 2664 2665 AG_sensitive ("anastasis_gtk_review_policy_treeview"); 2666 AG_show ("anastasis_gtk_progress_vbox"); 2667 AG_progress_update (); 2668 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 2669 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 2670 AG_show ("anastasis_gtk_main_control_vbox"); 2671 AG_show ("anastasis_gtk_main_window_save_as_button"); 2672 AG_show ("anastasis_gtk_challenge_frame"); 2673 AG_show ("anastasis_gtk_main_window_prev_button"); 2674 AG_hide ("anastasis_gtk_main_window_quit_button"); 2675 AG_hide ("anastasis_gtk_main_window_forward_button"); 2676 } 2677 2678 2679 /** 2680 * An Anastasis provider requires payment for a challenge. 2681 * Give opportunity to the user to pay. 2682 */ 2683 static void 2684 action_challenge_paying (void) 2685 { 2686 json_t *pprs; 2687 json_t *ppr; 2688 const char *uuid; 2689 bool found = false; 2690 const char *ps; 2691 2692 AG_hide_all_frames (); 2693 AG_list_clear ("unpaid_qrcodes_liststore"); 2694 pprs = json_object_get (AG_redux_state, 2695 "challenge_feedback"); 2696 json_object_foreach (pprs, uuid, ppr) 2697 { 2698 const char *state; 2699 const char *payto = NULL; 2700 const char *provider = NULL; 2701 struct GNUNET_JSON_Specification spec[] = { 2702 GNUNET_JSON_spec_string ("taler_pay_uri", 2703 &payto), 2704 GNUNET_JSON_spec_mark_optional ( 2705 GNUNET_JSON_spec_string ("provider", 2706 &provider), 2707 NULL), 2708 GNUNET_JSON_spec_string ("payment_secret", 2709 &ps), 2710 GNUNET_JSON_spec_end () 2711 }; 2712 2713 /* The state must be tested before the payment details are parsed: the 2714 other feedback states have no payment secret, and the reducer calls 2715 this one "taler-payment" (it was "payment" before Anastasis v0.3.0). */ 2716 state = json_string_value (json_object_get (ppr, 2717 "state")); 2718 if ( (NULL == state) || 2719 (0 != strcmp (state, 2720 "taler-payment")) ) 2721 continue; /* this challenge does not want to be paid for */ 2722 if (GNUNET_OK != 2723 GNUNET_JSON_parse (ppr, 2724 spec, 2725 NULL, NULL)) 2726 { 2727 GNUNET_break (0); 2728 json_dumpf (ppr, 2729 stderr, 2730 JSON_INDENT (2)); 2731 continue; 2732 } 2733 found = true; 2734 add_qrcode (payto, 2735 (NULL != provider) ? provider : ""); 2736 break; 2737 } 2738 2739 if (found) 2740 { 2741 json_t *args; 2742 struct GNUNET_TIME_Relative timeout; 2743 2744 timeout = GNUNET_TIME_UNIT_MINUTES; 2745 GNUNET_assert (NULL == AG_ra); 2746 args = GNUNET_JSON_PACK ( 2747 GNUNET_JSON_pack_time_rel ("timeout", 2748 timeout), 2749 GNUNET_JSON_pack_string ("payment_secret", 2750 ps)); 2751 AG_ra = ANASTASIS_redux_action (AG_redux_state, 2752 "pay", 2753 args, 2754 &AG_action_cb, 2755 NULL); 2756 json_decref (args); 2757 } 2758 else 2759 { 2760 AG_error ("ERROR: Internal error: should pay, but do not know what"); 2761 } 2762 AG_show ("anastasis_gtk_progress_vbox"); 2763 AG_progress_update (); 2764 AG_show ("anastasis_gtk_recovery_progress_scrolled_window"); 2765 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 2766 AG_show ("anastasis_gtk_pay_frame"); 2767 AG_show ("anastasis_gtk_main_control_vbox"); 2768 AG_sensitive ("anastasis_gtk_main_window_prev_button"); 2769 AG_show ("anastasis_gtk_main_window_prev_button"); 2770 AG_hide ("anastasis_gtk_main_window_forward_button"); 2771 } 2772 2773 2774 /** 2775 * Render challenge feedback for challenge @a uuid_str in a dialog of 2776 * @a builder in the label under @a target_widget. 2777 * 2778 * Useful in case the operation previously failed at the 2779 * server and we have some useful information to return 2780 * to the user. 2781 * 2782 * FIXME: only do it AFTER the first attempt of the 2783 * user to enter a code, and/or change what the 2784 * server returns so we do NOT render a confusing 2785 * error message on first use! 2786 * 2787 * @param builder a builder to get widgets from 2788 * @param target_widget the widget to update 2789 * @param uuid_str the UUID to render feedback for 2790 */ 2791 static void 2792 render_feedback (GtkBuilder *builder, 2793 const char *target_widget, 2794 const char *uuid_str) 2795 { 2796 json_t *cf; 2797 json_t *cs; 2798 const char *state; 2799 const char *redirect_url = NULL; 2800 const char *hint = NULL; 2801 json_t *details = NULL; 2802 const char *taler_pay_uri = NULL; 2803 uint32_t ec = TALER_EC_NONE; 2804 uint32_t http_status = 0; 2805 bool hide = false; 2806 struct GNUNET_JSON_Specification spec[] = { 2807 GNUNET_JSON_spec_string ("state", 2808 &state), 2809 GNUNET_JSON_spec_mark_optional ( 2810 GNUNET_JSON_spec_string ("taler_pay_uri", 2811 &taler_pay_uri), 2812 NULL), 2813 GNUNET_JSON_spec_mark_optional ( 2814 GNUNET_JSON_spec_json ("details", 2815 &details), 2816 NULL), 2817 GNUNET_JSON_spec_mark_optional ( 2818 GNUNET_JSON_spec_string ("redirect_url", 2819 &redirect_url), 2820 NULL), 2821 GNUNET_JSON_spec_mark_optional ( 2822 GNUNET_JSON_spec_string ("hint", 2823 &hint), 2824 NULL), 2825 GNUNET_JSON_spec_mark_optional ( 2826 GNUNET_JSON_spec_uint32 ("http_status", 2827 &http_status), 2828 NULL), 2829 GNUNET_JSON_spec_mark_optional ( 2830 GNUNET_JSON_spec_uint32 ("error_code", 2831 &ec), 2832 NULL), 2833 GNUNET_JSON_spec_end () 2834 }; 2835 GtkLabel *elabel; 2836 char *msg; 2837 2838 cf = json_object_get (AG_redux_state, 2839 "challenge_feedback"); 2840 cs = json_object_get (cf, 2841 uuid_str); 2842 if (NULL == cs) 2843 return; 2844 2845 elabel = GTK_LABEL (gtk_builder_get_object (builder, 2846 target_widget)); 2847 if (NULL == elabel) 2848 { 2849 GNUNET_break (0); 2850 return; 2851 } 2852 if (GNUNET_OK != 2853 GNUNET_JSON_parse (cs, 2854 spec, 2855 NULL, NULL)) 2856 { 2857 GNUNET_break (0); 2858 gtk_label_set_text (elabel, 2859 _ ("INTERNAL ERROR: could not parse state")); 2860 gtk_widget_set_visible (GTK_WIDGET (elabel), 2861 TRUE); 2862 return; 2863 } 2864 if ( (0 == strcmp (state, 2865 "hint")) && 2866 (NULL != hint) ) 2867 { 2868 GNUNET_asprintf (&msg, 2869 _ ("Hint (#%u): %s"), 2870 (unsigned int) http_status, 2871 dgettext ("taler-exchange", 2872 hint)); 2873 } 2874 else if ( (0 == strcmp (state, 2875 "details")) && 2876 (NULL != details) ) 2877 { 2878 uint32_t code; 2879 const char *dhint = NULL; 2880 const char *detail = NULL; 2881 struct GNUNET_JSON_Specification ispec[] = { 2882 GNUNET_JSON_spec_uint32 ("code", 2883 &code), 2884 GNUNET_JSON_spec_mark_optional ( 2885 GNUNET_JSON_spec_string ("hint", 2886 &dhint), 2887 NULL), 2888 GNUNET_JSON_spec_mark_optional ( 2889 GNUNET_JSON_spec_string ("detail", 2890 &detail), 2891 NULL), 2892 GNUNET_JSON_spec_end () 2893 }; 2894 2895 if (GNUNET_OK != 2896 GNUNET_JSON_parse (details, 2897 ispec, 2898 NULL, NULL)) 2899 { 2900 GNUNET_break (0); 2901 json_dumpf (details, 2902 stderr, 2903 JSON_INDENT (2)); 2904 msg = GNUNET_strdup ( 2905 _ ("ERROR: failed to parse server JSON instructions")); 2906 } 2907 else 2908 { 2909 const char *ihint; 2910 const char *type = "Error"; 2911 2912 switch (code) 2913 { 2914 case TALER_EC_ANASTASIS_TRUTH_CHALLENGE_RESPONSE_REQUIRED: 2915 hide = true; 2916 type = "Hint"; 2917 break; 2918 default: 2919 break; 2920 } 2921 ihint = TALER_ErrorCode_get_hint (code); 2922 if ( (NULL != hint) && 2923 ( (NULL == ihint) || 2924 ('<' == ihint[0])) ) 2925 ihint = dhint; /* use server hint */ 2926 ihint = dgettext ("taler-exchange", 2927 ihint); 2928 if (NULL == detail) 2929 { 2930 if (NULL == ihint) 2931 GNUNET_asprintf (&msg, 2932 "%s #%u", 2933 type, 2934 (unsigned int) code); 2935 else 2936 GNUNET_asprintf (&msg, 2937 "%s #%u: %s", 2938 type, 2939 (unsigned int) code, 2940 ihint); 2941 } 2942 else 2943 { 2944 if (NULL == ihint) 2945 GNUNET_asprintf (&msg, 2946 "%s #%u (%s)", 2947 type, 2948 (unsigned int) code, 2949 detail); 2950 else 2951 GNUNET_asprintf (&msg, 2952 "%s #%u: %s (%s)", 2953 type, 2954 (unsigned int) code, 2955 ihint, 2956 detail); 2957 } 2958 } 2959 } 2960 else 2961 { 2962 GNUNET_asprintf (&msg, 2963 "ERROR: state `%s` with HTTP Status %u", 2964 state, 2965 (unsigned int) http_status); 2966 } 2967 gtk_label_set_text (elabel, 2968 msg); 2969 GNUNET_free (msg); 2970 if (hide) 2971 gtk_widget_set_visible (GTK_WIDGET (elabel), 2972 FALSE); 2973 else 2974 gtk_widget_set_visible (GTK_WIDGET (elabel), 2975 TRUE); 2976 GNUNET_JSON_parse_free (spec); 2977 } 2978 2979 2980 /** 2981 * Open dialog to allow user to answer security question. 2982 * 2983 * @param details details about the challenge 2984 * @return the dialog object, or NULL on error 2985 */ 2986 static GtkDialog * 2987 diag_question (const json_t *details) 2988 { 2989 GtkBuilder *builder; 2990 GtkDialog *ad; 2991 2992 builder = ANASTASIS_GTK_get_new_builder ( 2993 ANASTASIS_GTK_project_data (), 2994 "anastasis_gtk_challenge_question.ui", 2995 NULL); 2996 if (NULL == builder) 2997 { 2998 GNUNET_break (0); 2999 return NULL; 3000 } 3001 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3002 "anastasis_gtk_c_question_dialog")); 3003 { 3004 GtkLabel *label; 3005 const char *instructions; 3006 3007 label = GTK_LABEL (gtk_builder_get_object (builder, 3008 "security_question_label")); 3009 instructions = json_string_value (json_object_get (details, 3010 "instructions")); 3011 gtk_label_set_text (label, 3012 instructions); 3013 } 3014 { 3015 const char *uuid_str; 3016 3017 uuid_str = json_string_value (json_object_get (details, 3018 "uuid-display")); 3019 render_feedback (builder, 3020 "anastasis_gtk_c_question_error_label", 3021 uuid_str); 3022 } 3023 return ad; 3024 } 3025 3026 3027 /** 3028 * Create a dialog for the user to enter a PIN code. 3029 * 3030 * @param details details about the dialog to render 3031 * @return dialog object 3032 */ 3033 static GtkDialog * 3034 diag_code (const json_t *details) 3035 { 3036 GtkBuilder *builder; 3037 const char *instructions; 3038 const char *uuid_str; 3039 struct GNUNET_JSON_Specification spec[] = { 3040 GNUNET_JSON_spec_string ("instructions", 3041 &instructions), 3042 GNUNET_JSON_spec_string ("uuid-display", 3043 &uuid_str), 3044 GNUNET_JSON_spec_end () 3045 }; 3046 3047 if (GNUNET_OK != 3048 GNUNET_JSON_parse (details, 3049 spec, 3050 NULL, NULL)) 3051 { 3052 GNUNET_break (0); 3053 json_dumpf (details, 3054 stderr, 3055 JSON_INDENT (2)); 3056 return NULL; 3057 } 3058 3059 builder = ANASTASIS_GTK_get_new_builder ( 3060 ANASTASIS_GTK_project_data (), 3061 "anastasis_gtk_challenge_code.ui", 3062 NULL); 3063 if (NULL == builder) 3064 { 3065 GNUNET_break (0); 3066 return NULL; 3067 } 3068 { 3069 GtkLabel *label; 3070 3071 label = GTK_LABEL (gtk_builder_get_object (builder, 3072 "challenge_instructions_label")); 3073 gtk_label_set_text (label, 3074 instructions); 3075 label = GTK_LABEL (gtk_builder_get_object (builder, 3076 "anastasis_gtk_c_challenge_label")); 3077 gtk_label_set_text (label, 3078 uuid_str); 3079 render_feedback (builder, 3080 "anastasis_gtk_c_code_error_label", 3081 uuid_str); 3082 } 3083 { 3084 GtkDialog *ad; 3085 3086 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3087 "anastasis_gtk_c_code_dialog")); 3088 return ad; 3089 } 3090 } 3091 3092 3093 /** 3094 * Create a dialog for the user to enter an TOTP code. 3095 * 3096 * @param details details about the dialog to render 3097 * @return dialog object 3098 */ 3099 static GtkDialog * 3100 diag_totp (const json_t *details) 3101 { 3102 GtkBuilder *builder; 3103 const char *instructions; 3104 const char *uuid_str; 3105 struct GNUNET_JSON_Specification spec[] = { 3106 GNUNET_JSON_spec_string ("instructions", 3107 &instructions), 3108 GNUNET_JSON_spec_string ("uuid-display", 3109 &uuid_str), 3110 GNUNET_JSON_spec_end () 3111 }; 3112 3113 if (GNUNET_OK != 3114 GNUNET_JSON_parse (details, 3115 spec, 3116 NULL, NULL)) 3117 { 3118 GNUNET_break (0); 3119 json_dumpf (details, 3120 stderr, 3121 JSON_INDENT (2)); 3122 return NULL; 3123 } 3124 3125 builder = ANASTASIS_GTK_get_new_builder ( 3126 ANASTASIS_GTK_project_data (), 3127 "anastasis_gtk_challenge_totp.ui", 3128 NULL); 3129 if (NULL == builder) 3130 { 3131 GNUNET_break (0); 3132 return NULL; 3133 } 3134 { 3135 GtkLabel *label; 3136 3137 label = GTK_LABEL (gtk_builder_get_object (builder, 3138 "challenge_instructions_label")); 3139 gtk_label_set_text (label, 3140 instructions); 3141 } 3142 render_feedback (builder, 3143 "anastasis_gtk_c_totp_error_label", 3144 uuid_str); 3145 { 3146 GtkDialog *ad; 3147 3148 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3149 "anastasis_gtk_c_totp_dialog")); 3150 return ad; 3151 } 3152 } 3153 3154 3155 /** 3156 * Create a dialog for the user to make an IBAN transfer. 3157 * 3158 * @param details details about the dialog to render 3159 * @return dialog object 3160 */ 3161 static GtkDialog * 3162 diag_iban (const json_t *details) 3163 { 3164 GtkBuilder *builder; 3165 struct TALER_Amount amount; 3166 const char *credit_iban; 3167 const char *business; 3168 const char *subject; 3169 const char *uuid_str; 3170 const char *debit_iban_hint; 3171 struct GNUNET_JSON_Specification spec[] = { 3172 GNUNET_JSON_spec_string ("uuid", 3173 &uuid_str), 3174 GNUNET_JSON_spec_string ("instructions", 3175 &debit_iban_hint), 3176 GNUNET_JSON_spec_end () 3177 }; 3178 3179 if (GNUNET_OK != 3180 GNUNET_JSON_parse (details, 3181 spec, 3182 NULL, NULL)) 3183 { 3184 GNUNET_break (0); 3185 json_dumpf (details, 3186 stderr, 3187 JSON_INDENT (2)); 3188 return NULL; 3189 } 3190 { 3191 json_t *cf = json_object_get (AG_redux_state, 3192 "challenge_feedback"); 3193 json_t *ci = json_object_get (cf, 3194 uuid_str); 3195 json_t *cd = json_object_get (ci, 3196 "details"); 3197 struct GNUNET_JSON_Specification ispec[] = { 3198 TALER_JSON_spec_amount_any ("challenge_amount", 3199 &amount), 3200 GNUNET_JSON_spec_string ("credit_iban", 3201 &credit_iban), 3202 GNUNET_JSON_spec_string ("business_name", 3203 &business), 3204 GNUNET_JSON_spec_string ("wire_transfer_subject", 3205 &subject), 3206 GNUNET_JSON_spec_end () 3207 }; 3208 3209 if ( (NULL == cd) || 3210 (GNUNET_OK != 3211 GNUNET_JSON_parse (cd, 3212 ispec, 3213 NULL, NULL)) ) 3214 { 3215 GNUNET_break (0); 3216 json_dumpf (AG_redux_state, 3217 stderr, 3218 JSON_INDENT (2)); 3219 return NULL; 3220 } 3221 } 3222 3223 builder = ANASTASIS_GTK_get_new_builder ( 3224 ANASTASIS_GTK_project_data (), 3225 "anastasis_gtk_challenge_iban.ui", 3226 NULL); 3227 if (NULL == builder) 3228 { 3229 GNUNET_break (0); 3230 return NULL; 3231 } 3232 { 3233 GtkLabel *label; 3234 3235 label = GTK_LABEL (gtk_builder_get_object (builder, 3236 "debit_account_label")); 3237 gtk_label_set_text (label, 3238 debit_iban_hint); 3239 label = GTK_LABEL (gtk_builder_get_object (builder, 3240 "credit_account_label")); 3241 gtk_label_set_text (label, 3242 credit_iban); 3243 label = GTK_LABEL (gtk_builder_get_object (builder, 3244 "provider_name_label")); 3245 gtk_label_set_text (label, 3246 business); 3247 label = GTK_LABEL (gtk_builder_get_object (builder, 3248 "wire_transfer_subject_label")); 3249 gtk_label_set_text (label, 3250 subject); 3251 label = GTK_LABEL (gtk_builder_get_object (builder, 3252 "amount_label")); 3253 gtk_label_set_text (label, 3254 TALER_amount2s (&amount)); 3255 } 3256 3257 { 3258 GtkDialog *ad; 3259 3260 ad = GTK_DIALOG (gtk_builder_get_object (builder, 3261 "anastasis_gtk_c_iban_dialog")); 3262 return ad; 3263 } 3264 } 3265 3266 3267 /** 3268 * The user wants to solve the selected challenge. Launch the 3269 * dialog to allow the user to enter the solution. 3270 */ 3271 static void 3272 action_challenge_solving (void) 3273 { 3274 struct 3275 { 3276 const char *type; 3277 GtkDialog *(*ctor)(const json_t *details); 3278 } type_map [] = { 3279 { .type = gettext_noop ("question"), 3280 .ctor = &diag_question }, 3281 { .type = gettext_noop ("sms"), 3282 .ctor = &diag_code }, 3283 { .type = gettext_noop ("post"), 3284 .ctor = &diag_code }, 3285 { .type = gettext_noop ("email"), 3286 .ctor = &diag_code }, 3287 { .type = gettext_noop ("iban"), 3288 .ctor = &diag_iban }, 3289 { .type = gettext_noop ("totp"), 3290 .ctor = &diag_totp }, 3291 { .type = NULL, 3292 .ctor = NULL } 3293 }; 3294 const char *type; 3295 GtkDialog *diag; 3296 const char *uuid; 3297 const json_t *challenge; 3298 3299 /* This state only puts a dialog on top of the frame the previous state 3300 rendered, so the progress list is the one thing that must be updated. */ 3301 AG_progress_update (); 3302 uuid = json_string_value (json_object_get (AG_redux_state, 3303 "selected_challenge_uuid")); 3304 if (NULL == uuid) 3305 { 3306 GNUNET_break (0); 3307 return; 3308 } 3309 challenge = find_challenge_by_uuid (uuid); 3310 if (NULL == challenge) 3311 { 3312 GNUNET_break (0); 3313 return; 3314 } 3315 type = json_string_value (json_object_get (challenge, 3316 "type")); 3317 if (NULL == type) 3318 { 3319 GNUNET_break (0); 3320 return; 3321 } 3322 /* create dialog based on challenge type */ 3323 diag = NULL; 3324 for (unsigned int i = 0; NULL != type_map[i].type; i++) 3325 { 3326 if (0 != strcmp (type_map[i].type, 3327 type)) 3328 continue; 3329 diag = type_map[i].ctor (challenge); 3330 break; 3331 } 3332 if (NULL == diag) 3333 { 3334 GNUNET_break (0); 3335 return; 3336 } 3337 /* show dialog */ 3338 { 3339 GtkRoot *toplevel; 3340 GtkWidget *box; 3341 3342 box = GTK_WIDGET (GCG_get_main_window_object ( 3343 "anastasis_gtk_policy_vbox")); 3344 toplevel = gtk_widget_get_root (box); 3345 gtk_window_set_transient_for (GTK_WINDOW (diag), 3346 GTK_WINDOW (toplevel)); 3347 gtk_window_present (GTK_WINDOW (diag)); 3348 } 3349 } 3350 3351 3352 /** 3353 * The recovery process was finished. Show the recovered secret to the 3354 * user. 3355 */ 3356 static void 3357 action_recovery_finished (void) 3358 { 3359 const char *mime = NULL; 3360 const char *text = NULL; 3361 const char *name = NULL; 3362 void *data = NULL; 3363 size_t data_size = 0; 3364 const json_t *cs; 3365 struct GNUNET_JSON_Specification spec[] = { 3366 GNUNET_JSON_spec_mark_optional ( 3367 GNUNET_JSON_spec_string ("mime", 3368 &mime), 3369 NULL), 3370 GNUNET_JSON_spec_mark_optional ( 3371 GNUNET_JSON_spec_string ("text", 3372 &text), 3373 NULL), 3374 GNUNET_JSON_spec_mark_optional ( 3375 GNUNET_JSON_spec_varsize ("value", 3376 &data, 3377 &data_size), 3378 NULL), 3379 GNUNET_JSON_spec_end () 3380 }; 3381 GdkPixbuf *pb; 3382 GtkImage *img; 3383 3384 AG_hide_all_frames (); 3385 name = json_string_value (json_object_get (json_object_get (AG_redux_state, 3386 "recovery_information"), 3387 "secret_name")); 3388 3389 cs = json_object_get (AG_redux_state, 3390 "core_secret"); 3391 GNUNET_assert (NULL != cs); 3392 GNUNET_assert (GNUNET_OK == 3393 GNUNET_JSON_parse (cs, 3394 spec, 3395 NULL, NULL)); 3396 AG_hide ("anastasis_gtk_secret_copy_button"); 3397 update_label ("anastasis_gtk_secret_value_label", 3398 text); 3399 if ( (NULL != name) && 3400 (0 != strlen (name)) ) 3401 update_label ("recovery_secret_name_value_label", 3402 name); 3403 else 3404 update_label ("recovery_secret_name_value_label", 3405 _ ("You did not name this secret")); 3406 if ( (0 == strncasecmp (mime, 3407 "text/", 3408 strlen ("text/"))) || 3409 (0 == strncasecmp (mime, 3410 "image/", 3411 strlen ("image/"))) || 3412 (NULL != text) ) 3413 { 3414 /* images and text can be copied */ 3415 AG_show ("anastasis_gtk_secret_copy_button"); 3416 } 3417 pb = NULL; 3418 img = GTK_IMAGE (GCG_get_main_window_object ( 3419 "anastasis_gtk_secret_qr_image")); 3420 if (NULL != text) 3421 { 3422 pb = AG_setup_qrcode (GTK_WIDGET (img), 3423 text, 3424 strlen (text)); 3425 } 3426 else 3427 { 3428 pb = AG_setup_qrcode (GTK_WIDGET (img), 3429 data, 3430 data_size); 3431 } 3432 if (NULL != pb) 3433 { 3434 AG_image_set_from_pixbuf (img, 3435 pb); 3436 g_object_unref (pb); 3437 } 3438 else 3439 { 3440 AG_hide ("anastasis_gtk_secret_qr_image"); 3441 } 3442 GNUNET_JSON_parse_free (spec); 3443 AG_hide ("anastasis_gtk_progress_vbox"); 3444 AG_hide ("anastasis_gtk_recovery_progress_scrolled_window"); 3445 AG_hide ("anastasis_gtk_backup_progress_scrolled_window"); 3446 AG_show ("anastasis_gtk_completed_frame"); 3447 AG_hide ("anastasis_gtk_backup_complete_box"); 3448 AG_hide ("anastasis_gtk_success_backup_label"); 3449 AG_show ("anastasis_gtk_success_recovery_box"); 3450 AG_show ("anastasis_gtk_main_control_vbox"); 3451 AG_hide ("anastasis_gtk_main_window_save_as_button"); 3452 AG_show ("anastasis_gtk_restart_button"); 3453 AG_show ("anastasis_gtk_main_window_quit_button"); 3454 AG_hide ("anastasis_gtk_main_window_prev_button"); 3455 AG_hide ("anastasis_gtk_main_window_forward_button"); 3456 } 3457 3458 3459 /** 3460 * Function called with the results of #ANASTASIS_redux_action. 3461 * 3462 * @param cls closure 3463 * @param error_code Error code 3464 * @param response new state as result or config information of a provider 3465 */ 3466 void 3467 AG_action_cb (void *cls, 3468 enum TALER_ErrorCode error_code, 3469 json_t *response) 3470 { 3471 struct DispatchItem actions[] = { 3472 { .state = "CONTINENT_SELECTING", 3473 .action = &action_continent_selecting }, 3474 { .state = "COUNTRY_SELECTING", 3475 .action = &action_country_selecting }, 3476 { .state = "USER_ATTRIBUTES_COLLECTING", 3477 .action = &action_user_attributes_collecting }, 3478 { .state = "AUTHENTICATIONS_EDITING", 3479 .action = &action_authentications_editing }, 3480 { .state = "POLICIES_REVIEWING", 3481 .action = &action_policies_reviewing }, 3482 { .state = "SECRET_EDITING", 3483 .action = &action_secret_editing }, 3484 { .state = "TRUTHS_PAYING", 3485 .action = &action_truths_paying }, 3486 { .state = "POLICIES_PAYING", 3487 .action = &action_policies_paying }, 3488 { .state = "BACKUP_FINISHED", 3489 .action = &action_backup_finished }, 3490 { .state = "SECRET_SELECTING", 3491 .action = &action_secret_selecting }, 3492 { .state = "CHALLENGE_SELECTING", 3493 .action = &action_challenge_selecting }, 3494 { .state = "CHALLENGE_PAYING", 3495 .action = &action_challenge_paying }, 3496 { .state = "CHALLENGE_SOLVING", 3497 .action = &action_challenge_solving }, 3498 { .state = "RECOVERY_FINISHED", 3499 .action = &action_recovery_finished }, 3500 { .state = NULL, 3501 .action = NULL } 3502 }; 3503 3504 (void) cls; 3505 AG_ra = NULL; 3506 AG_thaw (); 3507 #if DEBUG 3508 fprintf (stderr, 3509 "Action result %d\n", 3510 error_code); 3511 json_dumpf (response, 3512 stderr, 3513 JSON_INDENT (2)); 3514 fprintf (stderr, 3515 "END action result %d\n", 3516 error_code); 3517 #endif 3518 if (TALER_EC_NONE != error_code) 3519 { 3520 /* FIXME: maybe also render 'detail' 3521 if present in state? 3522 See for example ~ anastasis_api_backup_redux.c:3082 */ 3523 AG_error ("Error #%d: %s\n", 3524 (int) error_code, 3525 TALER_ErrorCode_get_hint (error_code)); 3526 if (AG_in_action) 3527 { 3528 GNUNET_break (0); 3529 return; 3530 } 3531 } 3532 if ( (NULL != json_object_get (response, 3533 "backup_state")) || 3534 (NULL != json_object_get (response, 3535 "recovery_state")) ) 3536 { 3537 json_decref (AG_redux_state); 3538 AG_stop_long_action (); 3539 AG_redux_state = json_incref (response); 3540 } 3541 if ( (TALER_EC_ANASTASIS_TRUTH_UNKNOWN == error_code) || 3542 (TALER_EC_ANASTASIS_TRUTH_RATE_LIMITED == error_code) ) 3543 { 3544 /* special case: do not remain in previous (challenge selected) 3545 state but revert to challenge selecting */ 3546 GNUNET_assert (0 == 3547 json_object_set_new (AG_redux_state, 3548 "recovery_state", 3549 json_string ("CHALLENGE_SELECTING"))); 3550 } 3551 if ( (TALER_EC_ANASTASIS_REDUCER_NETWORK_FAILED == error_code) || 3552 (TALER_EC_ANASTASIS_REDUCER_POLICY_MALFORMED == error_code) || 3553 (TALER_EC_ANASTASIS_REDUCER_POLICY_LOOKUP_FAILED == error_code) ) 3554 { 3555 /* special case: do not remain in previous (enter identity) 3556 state but advance to secret selecting */ 3557 GNUNET_assert (0 == 3558 json_object_set_new (AG_redux_state, 3559 "recovery_state", 3560 json_string ("SECRET_SELECTING"))); 3561 } 3562 AG_in_action = true; 3563 if (GNUNET_OK == 3564 AG_dispatch (actions)) 3565 { 3566 AG_in_action = false; 3567 return; 3568 } 3569 AG_in_action = false; 3570 AG_error ("Unhandled state `%s/%s'", 3571 json_string_value (json_object_get (AG_redux_state, 3572 "backup_state")), 3573 json_string_value (json_object_get (AG_redux_state, 3574 "recovery_state"))); 3575 json_dumpf (AG_redux_state, 3576 stderr, 3577 JSON_INDENT (2)); 3578 json_decref (AG_redux_state); 3579 AG_redux_state = NULL; 3580 AG_hide_all_frames (); 3581 AG_show ("anastasis_gtk_start_frame"); 3582 }