anastasis-gtk_handle-auth-edit-provider-clicked.c (16708B)
1 /* 2 This file is part of anastasis-gtk. 3 Copyright (C) 2021 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 /** 22 * @file src/anastasis/anastasis-gtk_backup.c 23 * @brief Main function of anastasis-gtk 24 * @author Christian Grothoff 25 * @author Dennis Neufeld 26 */ 27 #include <gnunet/gnunet_util_lib.h> 28 #include "anastasis_gtk_util.h" 29 #include "anastasis-gtk_action.h" 30 #include "anastasis-gtk_helper.h" 31 #include "anastasis-gtk_handle-main-window-forward-clicked.h" 32 #include <sys/types.h> 33 #include <sys/wait.h> 34 #include <jansson.h> 35 #include <microhttpd.h> 36 37 38 /** 39 * Context for menu callbacks. 40 */ 41 struct MenuContext 42 { 43 /** 44 * Base URL of the selected provider. 45 */ 46 char *url; 47 48 }; 49 50 51 /** 52 * An item was selected from the context menu; destroy the menu shell. 53 * 54 * @param menushell menu to destroy 55 * @param user_data the 'struct MenuContext' of the menu 56 */ 57 static void 58 context_popup_selection_done (GtkMenuShell *menushell, 59 gpointer user_data) 60 { 61 struct MenuContext *ctx = user_data; 62 63 gtk_widget_destroy (GTK_WIDGET (menushell)); 64 GNUNET_free (ctx->url); 65 GNUNET_free (ctx); 66 } 67 68 69 /** 70 * Open @a url in a browser. 71 * 72 * @param url the URL to open 73 */ 74 static void 75 xdg_open (const char *url) 76 { 77 pid_t chld; 78 int status; 79 80 chld = fork (); 81 if (-1 == chld) 82 { 83 GNUNET_log_strerror (GNUNET_ERROR_TYPE_ERROR, 84 "fork"); 85 return; 86 } 87 if (0 == chld) 88 { 89 pid_t c2; 90 91 c2 = fork (); 92 if (-1 == c2) 93 _exit (EXIT_FAILURE); 94 if (0 != c2) 95 _exit (EXIT_SUCCESS); 96 execlp ("xdg-open", 97 "xdg-open", 98 url, 99 NULL); 100 execlp ("open", 101 "open", 102 url, 103 NULL); 104 GNUNET_log_strerror_file (GNUNET_ERROR_TYPE_ERROR, 105 "exec", 106 "open"); 107 _exit (EXIT_FAILURE); 108 } 109 waitpid (chld, &status, 0); 110 } 111 112 113 /** 114 * The user selected the 'view pp' menu. 115 * 116 * @param menuitem the selected menu 117 * @param user_data a `struct MenuContext` 118 */ 119 static void 120 view_terms_of_service (GtkMenuItem *menuitem, 121 gpointer user_data) 122 { 123 struct MenuContext *ctx = user_data; 124 char *tos; 125 126 GNUNET_asprintf (&tos, 127 "%sterms", 128 ctx->url); 129 xdg_open (tos); 130 GNUNET_free (tos); 131 } 132 133 134 /** 135 * The user selected the 'view tos' menu. 136 * 137 * @param menuitem the selected menu 138 * @param user_data a `struct MenuContext` 139 */ 140 static void 141 view_privacy_policy (GtkMenuItem *menuitem, 142 gpointer user_data) 143 { 144 struct MenuContext *ctx = user_data; 145 char *pp; 146 147 GNUNET_asprintf (&pp, 148 "%sprivacy", 149 ctx->url); 150 xdg_open (pp); 151 GNUNET_free (pp); 152 } 153 154 155 void 156 provider_toggle_callback (GtkCellRendererToggle *cell, 157 gchar *path_str, 158 gpointer user_data) 159 { 160 GtkBuilder *builder = GTK_BUILDER (user_data); 161 GtkTreeIter iter; 162 GtkTreePath *path; 163 gboolean enabled; 164 GtkTreeModel *tm; 165 166 tm = GTK_TREE_MODEL (gtk_builder_get_object (builder, 167 "provider_liststore")); 168 if (NULL == tm) 169 { 170 GNUNET_break (0); 171 return; 172 } 173 path = gtk_tree_path_new_from_string (path_str); 174 if (! gtk_tree_model_get_iter (tm, 175 &iter, 176 path)) 177 { 178 GNUNET_break (0); 179 return; 180 } 181 gtk_tree_model_get (tm, 182 &iter, 183 AG_PMC_PROVIDER_ENABLED, &enabled, 184 -1); 185 enabled = ! enabled; 186 gtk_list_store_set (GTK_LIST_STORE (tm), 187 &iter, 188 AG_PMC_PROVIDER_ENABLED, enabled, 189 -1); 190 gtk_tree_path_free (path); 191 } 192 193 194 /** 195 * User clicked on the tree view. If it was a right-click, show 196 * context menu to allow user to view PP or TOS. 197 * 198 * @param widget the tree view 199 * @param event the event 200 * @param user_data the builder 201 */ 202 gboolean 203 provider_tree_view_button_press_event_cb (GtkWidget *widget, 204 GdkEvent *event, 205 gpointer user_data) 206 { 207 GtkBuilder *builder = GTK_BUILDER (user_data); 208 GdkEventButton *event_button = (GdkEventButton *) event; 209 GtkTreeView *tv; 210 GtkTreeModel *tm; 211 GtkTreePath *path; 212 GtkTreeIter iter; 213 struct MenuContext *ctx; 214 GtkMenu *menu; 215 216 if ((GDK_BUTTON_PRESS != event->type) || 217 (3 != event_button->button)) 218 return FALSE; /* not a right-click */ 219 tm = GTK_TREE_MODEL (gtk_builder_get_object (builder, 220 "provider_liststore")); 221 if (NULL == tm) 222 { 223 GNUNET_break (0); 224 return FALSE; 225 } 226 tv = GTK_TREE_VIEW (gtk_builder_get_object (builder, 227 "provider_tree_view")); 228 if (! gtk_tree_view_get_path_at_pos (tv, 229 event_button->x, 230 event_button->y, 231 &path, 232 NULL, 233 NULL, 234 NULL)) 235 { 236 /* nothing selected */ 237 return FALSE; 238 } 239 if (! gtk_tree_model_get_iter (tm, 240 &iter, 241 path)) 242 { 243 /* not sure how we got a path but no iter... */ 244 GNUNET_break (0); 245 return FALSE; 246 } 247 gtk_tree_path_free (path); 248 ctx = GNUNET_new (struct MenuContext); 249 gtk_tree_model_get (tm, 250 &iter, 251 AG_PMC_PROVIDER_URL, &ctx->url, 252 -1); 253 menu = GTK_MENU (gtk_menu_new ()); 254 { 255 GtkWidget *child; 256 257 child = gtk_menu_item_new_with_label (_ ("View _privacy policy...")); 258 g_signal_connect (child, 259 "activate", 260 G_CALLBACK (&view_privacy_policy), 261 ctx); 262 gtk_label_set_use_underline (GTK_LABEL ( 263 gtk_bin_get_child (GTK_BIN (child))), 264 TRUE); 265 gtk_widget_show (child); 266 gtk_menu_shell_append (GTK_MENU_SHELL (menu), 267 child); 268 } 269 { 270 GtkWidget *child; 271 272 child = gtk_menu_item_new_with_label (_ ("View _terms of service...")); 273 g_signal_connect (child, 274 "activate", 275 G_CALLBACK (&view_terms_of_service), 276 ctx); 277 gtk_label_set_use_underline (GTK_LABEL ( 278 gtk_bin_get_child (GTK_BIN (child))), 279 TRUE); 280 gtk_widget_show (child); 281 gtk_menu_shell_append (GTK_MENU_SHELL (menu), 282 child); 283 } 284 285 g_signal_connect (menu, 286 "selection-done", 287 G_CALLBACK (&context_popup_selection_done), 288 ctx); 289 290 gtk_menu_popup_at_pointer (menu, 291 event); 292 293 return FALSE; 294 } 295 296 297 /** 298 * The user clicked the "add" button to add a new provider to the list. 299 * 300 * @param button the button object 301 * @param user_data the builder 302 */ 303 void 304 url_add_button_clicked_cb (GtkButton *button, 305 gpointer user_data) 306 { 307 GtkBuilder *builder = GTK_BUILDER (user_data); 308 GtkListStore *ls; 309 GtkEntry *entry; 310 const char *url; 311 312 ls = GTK_LIST_STORE (gtk_builder_get_object (builder, 313 "provider_liststore")); 314 GNUNET_assert (NULL != ls); 315 entry = GTK_ENTRY (gtk_builder_get_object (builder, 316 "url_entry")); 317 url = gtk_entry_get_text (entry); 318 gtk_list_store_insert_with_values (ls, 319 NULL, 320 -1, 321 AG_PMC_PROVIDER_URL, url, 322 AG_PMC_PROVIDER_STATUS, _ ("new"), 323 AG_PMC_PROVIDER_STATUS_COLOR, "blue", 324 AG_PMC_PROVIDER_ENABLED, true, 325 AG_PMC_PROVIDER_SENSITIVE, false, 326 AG_PMC_PROVIDER_NOT_SENSITIVE, true, 327 AG_PMC_PROVIDER_NAME, url, 328 -1); 329 gtk_entry_set_text (entry, 330 ""); 331 } 332 333 334 /** 335 * The user changed the URL of a provider to be possibly added. 336 * Validate the syntax and if it is a valid URL, enable the button. 337 * 338 * @param entry the edited widget 339 * @param user_data the dialog builder 340 */ 341 void 342 url_entry_changed_cb (GtkEntry *entry, 343 gpointer user_data) 344 { 345 GtkBuilder *builder = GTK_BUILDER (user_data); 346 GtkWidget *button; 347 const char *url; 348 349 button = GTK_WIDGET (gtk_builder_get_object (builder, 350 "add_button")); 351 url = gtk_entry_get_text (entry); 352 gtk_widget_set_sensitive (button, 353 (0 == strncasecmp (url, 354 "http://", 355 strlen ("http://"))) || 356 (0 == strncasecmp (url, 357 "https://", 358 strlen ("https://")))); 359 } 360 361 362 /** 363 * Function called from the edit-provider dialog upon completion. 364 * 365 * @param dialog the pseudonym selection dialog 366 * @param response_id response code from the dialog 367 * @param user_data the builder of the dialog 368 */ 369 void 370 edit_provider_dialog_response_cb (GtkDialog *dialog, 371 gint response_id, 372 gpointer user_data) 373 { 374 GtkBuilder *builder = GTK_BUILDER (user_data); 375 GtkTreeModel *tm; 376 GtkTreeIter iter; 377 json_t *args; 378 379 if (GTK_RESPONSE_APPLY != response_id) 380 { 381 gtk_widget_destroy (GTK_WIDGET (dialog)); 382 g_object_unref (G_OBJECT (builder)); 383 return; 384 } 385 tm = GTK_TREE_MODEL (gtk_builder_get_object (builder, 386 "provider_liststore")); 387 if (NULL == tm) 388 { 389 GNUNET_break (0); 390 return; 391 } 392 args = json_object (); 393 if (gtk_tree_model_get_iter_first (tm, 394 &iter)) 395 do { 396 gchar *url; 397 gboolean enabled; 398 399 gtk_tree_model_get (tm, 400 &iter, 401 AG_PMC_PROVIDER_URL, &url, 402 AG_PMC_PROVIDER_ENABLED, &enabled, 403 -1); 404 GNUNET_assert (0 == 405 json_object_set_new ( 406 args, 407 url, 408 GNUNET_JSON_PACK ( 409 GNUNET_JSON_pack_string ("status", 410 enabled 411 ? "not-contacted" 412 : "disabled")))); 413 g_free (url); 414 } 415 while (gtk_tree_model_iter_next (tm, 416 &iter)); 417 gtk_widget_destroy (GTK_WIDGET (dialog)); 418 g_object_unref (G_OBJECT (builder)); 419 AG_freeze (); 420 AG_ra = ANASTASIS_redux_action (AG_redux_state, 421 "add_provider", 422 args, 423 &AG_action_cb, 424 NULL); 425 json_decref (args); 426 } 427 428 429 /** 430 * Callback invoked if the the "Edit"-provider list button is clicked. 431 * 432 * @param object 433 * @param user_data unused 434 */ 435 void 436 anastasis_gtk_edit_provider_list_clicked_cb (GtkButton *object, 437 gpointer user_data) 438 { 439 GtkWidget *ad; 440 GtkBuilder *builder; 441 GtkListStore *ls; 442 json_t *providers; 443 444 builder = ANASTASIS_GTK_get_new_builder ( 445 ANASTASIS_GTK_project_data (), 446 "anastasis_gtk_edit_providers.glade", 447 NULL); 448 if (NULL == builder) 449 { 450 GNUNET_break (0); 451 return; 452 } 453 ls = GTK_LIST_STORE (gtk_builder_get_object (builder, 454 "provider_liststore")); 455 providers = json_object_get (AG_redux_state, 456 "authentication_providers"); 457 { 458 const char *url; 459 const json_t *provider; 460 json_object_foreach (providers, url, provider) 461 { 462 uint32_t http_code = 0; 463 uint32_t ec = TALER_EC_NONE; 464 struct TALER_Amount ll; 465 const char *status; 466 const char *name = NULL; 467 struct GNUNET_JSON_Specification spec[] = { 468 GNUNET_JSON_spec_string ("status", 469 &status), 470 GNUNET_JSON_spec_mark_optional ( 471 GNUNET_JSON_spec_uint32 ("http_status", 472 &http_code), 473 NULL), 474 GNUNET_JSON_spec_mark_optional ( 475 GNUNET_JSON_spec_string ("business_name", 476 &name), 477 NULL), 478 GNUNET_JSON_spec_mark_optional ( 479 TALER_JSON_spec_amount_any ("liability_limit", 480 &ll), 481 NULL), 482 GNUNET_JSON_spec_mark_optional ( 483 GNUNET_JSON_spec_uint32 ("error_code", 484 &ec), 485 NULL), 486 GNUNET_JSON_spec_end () 487 }; 488 char *status_str; 489 const char *color; 490 bool sensitive = false; 491 const char *ll_s = NULL; 492 493 memset (&ll, 494 0, 495 sizeof (ll)); 496 if (GNUNET_OK != 497 GNUNET_JSON_parse (provider, 498 spec, 499 NULL, NULL)) 500 { 501 GNUNET_break (0); 502 json_dumpf (provider, 503 stderr, 504 JSON_INDENT (2)); 505 continue; 506 } 507 if ( (MHD_HTTP_OK == http_code) && 508 (0 != strcmp (status, 509 "disabled")) ) 510 { 511 status_str = GNUNET_strdup (_ ("available")); 512 color = "green"; 513 sensitive = true; 514 if (GNUNET_OK == 515 TALER_amount_is_valid (&ll)) 516 ll_s = TALER_amount2s (&ll); 517 else 518 GNUNET_break (0); 519 } 520 else if ( (0 == http_code) && 521 (0 != strcmp (status, 522 "disabled")) ) 523 { 524 GNUNET_asprintf (&status_str, 525 _ ("Network failure: %s (#%u)"), 526 TALER_ErrorCode_get_hint (ec), 527 (unsigned int) ec); 528 color = "red"; 529 } 530 else if (0 == strcmp (status, 531 "disabled")) 532 { 533 GNUNET_asprintf (&status_str, 534 _ ("disabled")); 535 color = "blue"; 536 sensitive = true; 537 } 538 else 539 { 540 GNUNET_asprintf (&status_str, 541 _ ("HTTP %s (%u): %s (#%u)"), 542 MHD_get_reason_phrase_for (http_code), 543 (unsigned int) http_code, 544 TALER_ErrorCode_get_hint (ec), 545 (unsigned int) ec); 546 color = "red"; 547 } 548 if (NULL == name) 549 name = url; 550 gtk_list_store_insert_with_values ( 551 ls, 552 NULL, 553 -1, 554 AG_PMC_PROVIDER_URL, url, 555 AG_PMC_PROVIDER_STATUS, status_str, 556 AG_PMC_PROVIDER_STATUS_COLOR, color, 557 AG_PMC_PROVIDER_LIABILITY_LIMIT, ll_s, 558 AG_PMC_PROVIDER_ENABLED, (0 != strcmp (status, 559 "disabled")), 560 AG_PMC_PROVIDER_SENSITIVE, sensitive, 561 AG_PMC_PROVIDER_NOT_SENSITIVE, ! sensitive, 562 AG_PMC_PROVIDER_NAME, name, 563 -1); 564 GNUNET_free (status_str); 565 } 566 } 567 ad = GTK_WIDGET (gtk_builder_get_object (builder, 568 "edit_provider_dialog")); 569 { 570 GtkWidget *toplevel; 571 572 toplevel = gtk_widget_get_toplevel (GTK_WIDGET (object)); 573 gtk_window_set_transient_for (GTK_WINDOW (ad), 574 GTK_WINDOW (toplevel)); 575 gtk_window_present (GTK_WINDOW (ad)); 576 } 577 }