anastasis-gtk_pe-edit-policy.c (14998B)
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_pe-edit-policy.c 23 * @brief Handle request to interactively edit policy 24 * @author Christian Grothoff 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_helper.h" 31 #include "anastasis-gtk_pe.h" 32 #include <jansson.h> 33 34 35 /** 36 * Context for the edit dialog. 37 */ 38 struct EditDialogContext; 39 40 /** 41 * Information we track per line in the grid. 42 */ 43 struct LineContext 44 { 45 /** 46 * Kept in a DLL. 47 */ 48 struct LineContext *next; 49 50 /** 51 * Kept in a DLL. 52 */ 53 struct LineContext *prev; 54 55 /** 56 * Context this line context belongs with. 57 */ 58 struct EditDialogContext *edc; 59 60 /** 61 * Our combo box. 62 */ 63 GtkComboBox *cb; 64 65 /** 66 * Model for our combo box. 67 */ 68 GtkTreeModel *model; 69 70 /** 71 * Challenge index for this line. 72 */ 73 unsigned int cindex; 74 75 /** 76 * Is this challenge used? 77 */ 78 bool on; 79 }; 80 81 82 /** 83 * Context for the edit dialog. 84 */ 85 struct EditDialogContext 86 { 87 /** 88 * Builder of the dialog. 89 */ 90 GtkBuilder *builder; 91 92 /** 93 * Head of line contexts for this dialog 94 */ 95 struct LineContext *lc_head; 96 97 /** 98 * Tail of line contexts for this dialog 99 */ 100 struct LineContext *lc_tail; 101 102 /** 103 * Policy index. UINT_MAX for a new policy. 104 */ 105 unsigned int pindex; 106 107 }; 108 109 110 /** 111 * Handle the response from the edit dialog. 112 * 113 * @param dialog the dialog 114 * @param response_id what the user's action was 115 * @param user_data a `struct EditDialogContext` 116 */ 117 void 118 anastasis_gtk_policy_edit_dialog_response_cb ( 119 GtkDialog *dialog, 120 gint response_id, 121 gpointer user_data) 122 { 123 struct EditDialogContext *edc = user_data; 124 125 if (GTK_RESPONSE_OK == response_id) 126 { 127 json_t *policy; 128 129 policy = json_array (); 130 GNUNET_assert (NULL != policy); 131 for (struct LineContext *lctx = edc->lc_head; 132 NULL != lctx; 133 lctx = lctx->next) 134 { 135 GtkTreeIter iter; 136 gchar *url; 137 138 if (! lctx->on) 139 continue; 140 if (! gtk_combo_box_get_active_iter (lctx->cb, 141 &iter)) 142 { 143 GNUNET_break (0); 144 continue; 145 } 146 gtk_tree_model_get (lctx->model, 147 &iter, 148 0, &url, 149 -1); 150 GNUNET_assert (0 == 151 json_array_append_new ( 152 policy, 153 GNUNET_JSON_PACK ( 154 GNUNET_JSON_pack_uint64 ("authentication_method", 155 lctx->cindex), 156 GNUNET_JSON_pack_string ("provider", 157 url)))); 158 g_free (url); 159 } 160 if (UINT_MAX == edc->pindex) 161 { 162 json_t *args; 163 164 args = GNUNET_JSON_PACK ( 165 GNUNET_JSON_pack_array_steal ("policy", 166 policy)); 167 AG_ra = ANASTASIS_redux_action (AG_redux_state, 168 "add_policy", 169 args, 170 &AG_action_cb, 171 NULL); 172 json_decref (args); 173 } 174 else 175 { 176 json_t *args; 177 178 args = GNUNET_JSON_PACK ( 179 GNUNET_JSON_pack_uint64 ("policy_index", 180 edc->pindex), 181 GNUNET_JSON_pack_array_steal ("policy", 182 policy)); 183 AG_ra = ANASTASIS_redux_action (AG_redux_state, 184 "update_policy", 185 args, 186 &AG_action_cb, 187 NULL); 188 json_decref (args); 189 } 190 } 191 /* clean up */ 192 { 193 struct LineContext *lctx; 194 195 while (NULL != (lctx = edc->lc_head)) 196 { 197 GNUNET_CONTAINER_DLL_remove (edc->lc_head, 198 edc->lc_tail, 199 lctx); 200 GNUNET_free (lctx); 201 } 202 } 203 gtk_widget_destroy (GTK_WIDGET (dialog)); 204 g_object_unref (G_OBJECT (edc->builder)); 205 GNUNET_free (edc); 206 } 207 208 209 /** 210 * The user changed an entry in the combo boxy of an edit 211 * dialog. Update the ability to confirm the selection: 212 * if at least one authentication method is selected, the 213 * OK button should be sensitive. 214 * 215 * @param widget the combo box that was changed 216 * @param user_data the `struct EditDialogContext` 217 */ 218 static void 219 combo_box_changed_cb ( 220 GtkComboBox *widget, 221 gpointer user_data) 222 { 223 struct LineContext *lc = user_data; 224 struct EditDialogContext *edc = lc->edc; 225 226 /* Update our line context's on/off flag */ 227 { 228 GtkTreeIter iter; 229 230 if (! gtk_combo_box_get_active_iter (lc->cb, 231 &iter)) 232 { 233 GNUNET_break (0); 234 } 235 else 236 { 237 gchar *url; 238 239 gtk_tree_model_get (lc->model, 240 &iter, 241 0, &url, 242 -1); 243 lc->on = (0 != 244 strcmp (_ ("<off>"), 245 url)); 246 g_free (url); 247 } 248 } 249 /* finally, update "OK" button sensitivity */ 250 { 251 GtkWidget *ok_button; 252 bool legal = false; 253 254 for (struct LineContext *lctx = edc->lc_head; 255 NULL != lctx; 256 lctx = lctx->next) 257 legal |= lctx->on; 258 ok_button = GTK_WIDGET (gtk_builder_get_object (edc->builder, 259 "ok_button")); 260 gtk_widget_set_sensitive (ok_button, 261 legal); 262 } 263 } 264 265 266 /** 267 * Check if the authentication provider @a ap offers 268 * authentications of type @a type. If so, return true. 269 * 270 * @param type method to check for 271 * @param ap provider to check against 272 * @return true if @a ap offers @a type 273 */ 274 static bool 275 ap_matches (const char *type, 276 json_t *ap) 277 { 278 json_t *methods; 279 size_t index; 280 json_t *method; 281 const char *status; 282 283 status = json_string_value (json_object_get (ap, 284 "status")); 285 if (0 != strcasecmp (status, 286 "ok")) 287 { 288 /* provider is down, cannot proceed */ 289 return false; 290 } 291 methods = json_object_get (ap, 292 "methods"); 293 GNUNET_break (NULL != methods); 294 json_array_foreach (methods, index, method) 295 { 296 const char *offer; 297 298 offer = json_string_value (json_object_get (method, 299 "type")); 300 if (NULL == offer) 301 { 302 GNUNET_break (0); 303 continue; 304 } 305 if (0 == strcasecmp (type, 306 offer)) 307 return true; 308 } 309 return false; 310 } 311 312 313 /** 314 * Create a GtkListStore containing all of the URLs 315 * of Anastasis providers offering @a type as an 316 * authentication method. 317 * 318 * @return the model 319 */ 320 static GtkTreeModel * 321 make_model (const char *type) 322 { 323 GtkListStore *ls; 324 json_t *aps; 325 const char *url; 326 json_t *ap; 327 328 ls = gtk_list_store_new (1, 329 G_TYPE_STRING); 330 gtk_list_store_insert_with_values (ls, 331 NULL, 332 -1, 333 0, _ ("<off>"), 334 -1); 335 aps = json_object_get (AG_redux_state, 336 "authentication_providers"); 337 GNUNET_break (NULL != aps); 338 json_object_foreach (aps, url, ap) { 339 if (ap_matches (type, 340 ap)) 341 { 342 gtk_list_store_insert_with_values (ls, 343 NULL, 344 -1, 345 0, url, 346 -1); 347 } 348 } 349 350 return GTK_TREE_MODEL (ls); 351 } 352 353 354 /** 355 * Select entry in @a cb based on the @a url. 356 * 357 * @param url provider to select 358 * @param lctx line to update 359 */ 360 static void 361 select_by_url (const char *url, 362 struct LineContext *lctx) 363 { 364 GtkTreeIter iter; 365 366 if (! gtk_tree_model_get_iter_first (lctx->model, 367 &iter)) 368 { 369 GNUNET_break (0); 370 return; 371 } 372 do { 373 gchar *have; 374 375 gtk_tree_model_get (lctx->model, 376 &iter, 377 0, &have, 378 -1); 379 if (0 == strcmp (have, 380 url)) 381 { 382 gtk_combo_box_set_active_iter (lctx->cb, 383 &iter); 384 lctx->on = true; 385 g_free (have); 386 return; 387 } 388 g_free (have); 389 } while (gtk_tree_model_iter_next (lctx->model, 390 &iter)); 391 GNUNET_break (0); /* not found */ 392 } 393 394 395 /** 396 * Select entry in @a cb based on the @a methods for 397 * challenge @a cindex. 398 * 399 * @param methods methods of policy to base selection on 400 * @param lctx line to update 401 */ 402 static void 403 select_by_policy (const json_t *methods, 404 struct LineContext *lctx) 405 { 406 size_t index; 407 json_t *method; 408 GtkTreeIter iter; 409 410 if (! gtk_tree_model_get_iter_first (lctx->model, 411 &iter)) 412 { 413 GNUNET_break (0); 414 return; 415 } 416 gtk_combo_box_set_active_iter (lctx->cb, 417 &iter); 418 json_array_foreach (methods, index, method) { 419 json_int_t am = json_integer_value (json_object_get (method, \ 420 "authentication_method")); 421 const char *url; 422 423 if (am != lctx->cindex) 424 continue; 425 url = json_string_value (json_object_get (method, 426 "provider")); 427 select_by_url (url, 428 lctx); 429 break; 430 } 431 } 432 433 434 void 435 AG_edit_policy (guint pindex) 436 { 437 struct EditDialogContext *edc; 438 GtkGrid *grid; 439 json_t *methods = NULL; 440 441 edc = GNUNET_new (struct EditDialogContext); 442 edc->builder = ANASTASIS_GTK_get_new_builder ( 443 ANASTASIS_GTK_project_data (), 444 "anastasis_gtk_edit_policy.glade", 445 edc); 446 edc->pindex = pindex; 447 if (NULL == edc->builder) 448 { 449 GNUNET_break (0); 450 GNUNET_free (edc); 451 return; 452 } 453 if (UINT_MAX != pindex) 454 { 455 json_t *policies; 456 json_t *policy; 457 458 policies = json_object_get (AG_redux_state, 459 "policies"); 460 policy = json_array_get (policies, 461 pindex); 462 methods = json_object_get (policy, 463 "methods"); 464 GNUNET_break (NULL != methods); 465 } 466 grid = GTK_GRID (gtk_builder_get_object (edc->builder, 467 "policy_grid")); 468 { 469 json_t *ams = json_object_get (AG_redux_state, 470 "authentication_methods"); 471 json_t *am; 472 size_t index; 473 gint row = 1; 474 475 json_array_foreach (ams, index, am) { 476 const char *type; 477 const char *instructions; 478 struct GNUNET_JSON_Specification spec[] = { 479 GNUNET_JSON_spec_string ("type", 480 &type), 481 GNUNET_JSON_spec_string ("instructions", 482 &instructions), 483 GNUNET_JSON_spec_end () 484 }; 485 char *labels; 486 GtkWidget *label; 487 GtkWidget *cb; 488 struct LineContext *lctx; 489 490 if (GNUNET_OK != 491 GNUNET_JSON_parse (am, 492 spec, 493 NULL, NULL)) 494 { 495 GNUNET_break (0); 496 continue; 497 } 498 lctx = GNUNET_new (struct LineContext); 499 lctx->cindex = index; 500 GNUNET_asprintf (&labels, 501 "<b>%s</b>: %s", 502 type, 503 instructions); 504 label = gtk_label_new (NULL); 505 gtk_label_set_markup (GTK_LABEL (label), 506 labels); 507 GNUNET_free (labels); 508 lctx->model = make_model (type); 509 cb = gtk_combo_box_new_with_model (lctx->model); 510 lctx->cb = GTK_COMBO_BOX (cb); 511 { 512 GtkCellRenderer *renderer; 513 514 renderer = gtk_cell_renderer_text_new (); 515 gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (cb), 516 renderer, 517 true); 518 gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (cb), 519 renderer, 520 "text", 521 0); 522 } 523 lctx->edc = edc; 524 GNUNET_CONTAINER_DLL_insert (edc->lc_head, 525 edc->lc_tail, 526 lctx); 527 g_object_connect (cb, 528 "signal::changed", 529 &combo_box_changed_cb, lctx, 530 NULL); 531 if (NULL != methods) 532 select_by_policy (methods, 533 lctx); 534 gtk_grid_insert_row (grid, 535 row); 536 gtk_widget_show (label); 537 gtk_grid_attach (grid, 538 label, 539 0, 540 row, 541 1, 542 1); 543 g_object_set (cb, 544 "expand", 545 TRUE, 546 NULL); 547 gtk_widget_show (cb); 548 gtk_grid_attach (grid, 549 cb, 550 1, 551 row, 552 1, 553 1); 554 row++; 555 } 556 } 557 { 558 GtkWidget *toplevel; 559 GtkWidget *ad; 560 GtkWidget *anchor; 561 GtkRequisition req; 562 563 anchor = GTK_WIDGET (GCG_get_main_window_object ( 564 "anastasis_gtk_main_window_quit_button")); 565 toplevel = gtk_widget_get_toplevel (anchor); 566 ad = GTK_WIDGET (gtk_builder_get_object (edc->builder, 567 "anastasis_gtk_policy_edit_dialog") 568 ); 569 gtk_widget_get_preferred_size (ad, 570 NULL, 571 &req); 572 gtk_window_resize (GTK_WINDOW (ad), 573 req.width, 574 req.height); 575 gtk_window_set_transient_for (GTK_WINDOW (ad), 576 GTK_WINDOW (toplevel)); 577 gtk_window_present (GTK_WINDOW (ad)); 578 } 579 } 580 581 582 /* end of anastasis-gtk_pe-edit-policy.c */