anastasis-gtk_pe-edit-policy.c (14308B)
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 drop down. 62 */ 63 GtkDropDown *cb; 64 65 /** 66 * The provider URLs our drop down offers. 67 */ 68 GtkStringList *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 * Key under which the dialog's `struct EditDialogContext` is attached to its 112 * builder. GtkBuilder hands the builder itself to the signal handlers, so 113 * that is where we keep our own state. 114 */ 115 #define EDC_KEY "anastasis-edit-dialog-context" 116 117 118 /** 119 * Handle the response from the edit dialog. 120 * 121 * @param dialog the dialog 122 * @param response_id what the user's action was 123 * @param user_data the builder of the dialog 124 */ 125 void 126 anastasis_gtk_policy_edit_dialog_response_cb ( 127 GtkDialog *dialog, 128 gint response_id, 129 gpointer user_data) 130 { 131 struct EditDialogContext *edc = g_object_get_data (G_OBJECT (user_data), 132 EDC_KEY); 133 134 if (GTK_RESPONSE_OK == response_id) 135 { 136 json_t *policy; 137 138 policy = json_array (); 139 GNUNET_assert (NULL != policy); 140 for (struct LineContext *lctx = edc->lc_head; 141 NULL != lctx; 142 lctx = lctx->next) 143 { 144 guint selected; 145 146 if (! lctx->on) 147 continue; 148 selected = gtk_drop_down_get_selected (lctx->cb); 149 if (GTK_INVALID_LIST_POSITION == selected) 150 { 151 GNUNET_break (0); 152 continue; 153 } 154 GNUNET_assert (0 == 155 json_array_append_new ( 156 policy, 157 GNUNET_JSON_PACK ( 158 GNUNET_JSON_pack_uint64 ("authentication_method", 159 lctx->cindex), 160 GNUNET_JSON_pack_string ("provider", 161 gtk_string_list_get_string ( 162 lctx->model, 163 selected))))); 164 } 165 if (UINT_MAX == edc->pindex) 166 { 167 json_t *args; 168 169 args = GNUNET_JSON_PACK ( 170 GNUNET_JSON_pack_array_steal ("policy", 171 policy)); 172 AG_ra = ANASTASIS_redux_action (AG_redux_state, 173 "add_policy", 174 args, 175 &AG_action_cb, 176 NULL); 177 json_decref (args); 178 } 179 else 180 { 181 json_t *args; 182 183 args = GNUNET_JSON_PACK ( 184 GNUNET_JSON_pack_uint64 ("policy_index", 185 edc->pindex), 186 GNUNET_JSON_pack_array_steal ("policy", 187 policy)); 188 AG_ra = ANASTASIS_redux_action (AG_redux_state, 189 "update_policy", 190 args, 191 &AG_action_cb, 192 NULL); 193 json_decref (args); 194 } 195 } 196 /* clean up */ 197 { 198 struct LineContext *lctx; 199 200 while (NULL != (lctx = edc->lc_head)) 201 { 202 GNUNET_CONTAINER_DLL_remove (edc->lc_head, 203 edc->lc_tail, 204 lctx); 205 GNUNET_free (lctx); 206 } 207 } 208 gtk_window_destroy (GTK_WINDOW (dialog)); 209 g_object_unref (G_OBJECT (edc->builder)); 210 GNUNET_free (edc); 211 } 212 213 214 /** 215 * The user changed an entry in the drop down of an edit 216 * dialog. Update the ability to confirm the selection: 217 * if at least one authentication method is selected, the 218 * OK button should be sensitive. 219 * 220 * @param widget the drop down that was changed 221 * @param pspec unused 222 * @param user_data the `struct EditDialogContext` 223 */ 224 static void 225 drop_down_changed_cb ( 226 GtkDropDown *widget, 227 GParamSpec *pspec, 228 gpointer user_data) 229 { 230 struct LineContext *lc = user_data; 231 struct EditDialogContext *edc = lc->edc; 232 233 (void) widget; 234 (void) pspec; 235 /* Update our line context's on/off flag */ 236 { 237 guint selected = gtk_drop_down_get_selected (lc->cb); 238 239 if (GTK_INVALID_LIST_POSITION == selected) 240 { 241 GNUNET_break (0); 242 } 243 else 244 { 245 lc->on = (0 != 246 strcmp (_ ("<off>"), 247 gtk_string_list_get_string (lc->model, 248 selected))); 249 } 250 } 251 /* finally, update "OK" button sensitivity */ 252 { 253 GtkWidget *ok_button; 254 bool legal = false; 255 256 for (struct LineContext *lctx = edc->lc_head; 257 NULL != lctx; 258 lctx = lctx->next) 259 legal |= lctx->on; 260 ok_button = GTK_WIDGET (gtk_builder_get_object (edc->builder, 261 "ok_button")); 262 gtk_widget_set_sensitive (ok_button, 263 legal); 264 } 265 } 266 267 268 /** 269 * Check if the authentication provider @a ap offers 270 * authentications of type @a type. If so, return true. 271 * 272 * @param type method to check for 273 * @param ap provider to check against 274 * @return true if @a ap offers @a type 275 */ 276 static bool 277 ap_matches (const char *type, 278 json_t *ap) 279 { 280 json_t *methods; 281 size_t index; 282 json_t *method; 283 const char *status; 284 285 status = json_string_value (json_object_get (ap, 286 "status")); 287 if (0 != strcasecmp (status, 288 "ok")) 289 { 290 /* provider is down, cannot proceed */ 291 return false; 292 } 293 methods = json_object_get (ap, 294 "methods"); 295 GNUNET_break (NULL != methods); 296 json_array_foreach (methods, index, method) 297 { 298 const char *offer; 299 300 offer = json_string_value (json_object_get (method, 301 "type")); 302 if (NULL == offer) 303 { 304 GNUNET_break (0); 305 continue; 306 } 307 if (0 == strcasecmp (type, 308 offer)) 309 return true; 310 } 311 return false; 312 } 313 314 315 /** 316 * Create a list with all of the URLs of Anastasis providers offering 317 * @a type as an authentication method. 318 * 319 * @return the model 320 */ 321 static GtkStringList * 322 make_model (const char *type) 323 { 324 GtkStringList *sl; 325 json_t *aps; 326 const char *url; 327 json_t *ap; 328 329 sl = gtk_string_list_new (NULL); 330 gtk_string_list_append (sl, 331 _ ("<off>")); 332 aps = json_object_get (AG_redux_state, 333 "authentication_providers"); 334 GNUNET_break (NULL != aps); 335 json_object_foreach (aps, url, ap) { 336 if (ap_matches (type, 337 ap)) 338 { 339 gtk_string_list_append (sl, 340 url); 341 } 342 } 343 344 return sl; 345 } 346 347 348 /** 349 * Select entry in @a cb based on the @a url. 350 * 351 * @param url provider to select 352 * @param lctx line to update 353 */ 354 static void 355 select_by_url (const char *url, 356 struct LineContext *lctx) 357 { 358 guint n = g_list_model_get_n_items (G_LIST_MODEL (lctx->model)); 359 360 for (guint i = 0; i < n; i++) 361 { 362 if (0 == strcmp (gtk_string_list_get_string (lctx->model, 363 i), 364 url)) 365 { 366 gtk_drop_down_set_selected (lctx->cb, 367 i); 368 lctx->on = true; 369 return; 370 } 371 } 372 GNUNET_break (0); /* not found */ 373 } 374 375 376 /** 377 * Select entry in @a cb based on the @a methods for 378 * challenge @a cindex. 379 * 380 * @param methods methods of policy to base selection on 381 * @param lctx line to update 382 */ 383 static void 384 select_by_policy (const json_t *methods, 385 struct LineContext *lctx) 386 { 387 size_t index; 388 json_t *method; 389 390 /* default to the "<off>" entry we put first */ 391 gtk_drop_down_set_selected (lctx->cb, 392 0); 393 json_array_foreach (methods, index, method) { 394 json_int_t am = json_integer_value (json_object_get (method, \ 395 "authentication_method")); 396 const char *url; 397 398 if (am != lctx->cindex) 399 continue; 400 url = json_string_value (json_object_get (method, 401 "provider")); 402 select_by_url (url, 403 lctx); 404 break; 405 } 406 } 407 408 409 void 410 AG_edit_policy (guint pindex) 411 { 412 struct EditDialogContext *edc; 413 GtkGrid *grid; 414 json_t *methods = NULL; 415 416 edc = GNUNET_new (struct EditDialogContext); 417 edc->builder = ANASTASIS_GTK_get_new_builder ( 418 ANASTASIS_GTK_project_data (), 419 "anastasis_gtk_edit_policy.ui", 420 NULL); 421 edc->pindex = pindex; 422 if (NULL == edc->builder) 423 { 424 GNUNET_break (0); 425 GNUNET_free (edc); 426 return; 427 } 428 g_object_set_data (G_OBJECT (edc->builder), 429 EDC_KEY, 430 edc); 431 if (UINT_MAX != pindex) 432 { 433 json_t *policies; 434 json_t *policy; 435 436 policies = json_object_get (AG_redux_state, 437 "policies"); 438 policy = json_array_get (policies, 439 pindex); 440 methods = json_object_get (policy, 441 "methods"); 442 GNUNET_break (NULL != methods); 443 } 444 grid = GTK_GRID (gtk_builder_get_object (edc->builder, 445 "policy_grid")); 446 { 447 json_t *ams = json_object_get (AG_redux_state, 448 "authentication_methods"); 449 json_t *am; 450 size_t index; 451 gint row = 1; 452 453 json_array_foreach (ams, index, am) { 454 const char *type; 455 const char *instructions; 456 struct GNUNET_JSON_Specification spec[] = { 457 GNUNET_JSON_spec_string ("type", 458 &type), 459 GNUNET_JSON_spec_string ("instructions", 460 &instructions), 461 GNUNET_JSON_spec_end () 462 }; 463 char *labels; 464 GtkWidget *label; 465 GtkWidget *cb; 466 struct LineContext *lctx; 467 468 if (GNUNET_OK != 469 GNUNET_JSON_parse (am, 470 spec, 471 NULL, NULL)) 472 { 473 GNUNET_break (0); 474 continue; 475 } 476 lctx = GNUNET_new (struct LineContext); 477 lctx->cindex = index; 478 GNUNET_asprintf (&labels, 479 "<b>%s</b>: %s", 480 type, 481 instructions); 482 label = gtk_label_new (NULL); 483 gtk_label_set_markup (GTK_LABEL (label), 484 labels); 485 GNUNET_free (labels); 486 lctx->model = make_model (type); 487 /* the drop down takes over our reference to the model */ 488 cb = gtk_drop_down_new (G_LIST_MODEL (lctx->model), 489 NULL); 490 lctx->cb = GTK_DROP_DOWN (cb); 491 lctx->edc = edc; 492 GNUNET_CONTAINER_DLL_insert (edc->lc_head, 493 edc->lc_tail, 494 lctx); 495 g_object_connect (cb, 496 "signal::notify::selected", 497 &drop_down_changed_cb, lctx, 498 NULL); 499 if (NULL != methods) 500 select_by_policy (methods, 501 lctx); 502 gtk_grid_insert_row (grid, 503 row); 504 gtk_grid_attach (grid, 505 label, 506 0, 507 row, 508 1, 509 1); 510 gtk_widget_set_hexpand (cb, 511 TRUE); 512 gtk_widget_set_vexpand (cb, 513 TRUE); 514 gtk_grid_attach (grid, 515 cb, 516 1, 517 row, 518 1, 519 1); 520 row++; 521 } 522 } 523 { 524 GtkRoot *toplevel; 525 GtkWidget *ad; 526 GtkWidget *anchor; 527 GtkRequisition req; 528 529 anchor = GTK_WIDGET (GCG_get_main_window_object ( 530 "anastasis_gtk_main_window_quit_button")); 531 toplevel = gtk_widget_get_root (anchor); 532 ad = GTK_WIDGET (gtk_builder_get_object (edc->builder, 533 "anastasis_gtk_policy_edit_dialog") 534 ); 535 gtk_widget_get_preferred_size (ad, 536 NULL, 537 &req); 538 gtk_window_set_default_size (GTK_WINDOW (ad), 539 req.width, 540 req.height); 541 gtk_window_set_transient_for (GTK_WINDOW (ad), 542 GTK_WINDOW (toplevel)); 543 gtk_window_present (GTK_WINDOW (ad)); 544 } 545 } 546 547 548 /* end of anastasis-gtk_pe-edit-policy.c */