GCC Code Coverage Report


Directory: ./
File: panels/system/users/cc-add-user-dialog.c
Date: 2024-05-04 07:58:27
Exec Total Coverage
Lines: 0 407 0.0%
Functions: 0 37 0.0%
Branches: 0 233 0.0%

Line Branch Exec Source
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2 *
3 * Copyright 2009-2010 Red Hat, Inc,
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, see <http://www.gnu.org/licenses/>.
17 *
18 * Written by: Matthias Clasen <mclasen@redhat.com>
19 */
20
21 #include "config.h"
22
23 #include <adwaita.h>
24 #include <glib.h>
25 #include <glib/gi18n.h>
26 #include <gtk/gtk.h>
27 #include <act/act.h>
28
29 #include "cc-add-user-dialog.h"
30 #include "cc-list-row.h"
31 #include "user-utils.h"
32 #include "pw-utils.h"
33
34 #define PASSWORD_CHECK_TIMEOUT 600
35
36 static void dialog_validate (CcAddUserDialog *self);
37
38 static void add_button_clicked_cb (CcAddUserDialog *self);
39
40 struct _CcAddUserDialog {
41 AdwDialog parent_instance;
42
43 GtkButton *add_button;
44 GtkSwitch *local_account_type_switch;
45 GtkEntry *local_name_entry;
46 GtkImage *local_name_status_icon;
47 AdwPreferencesPage *local_page;
48 AdwActionRow *local_password_row;
49 GtkImage *local_password_status_icon;
50 GtkLevelBar *local_strength_indicator;
51 GtkComboBoxText *local_username_combo;
52 GtkListStore *local_username_model;
53 GtkPasswordEntry *local_password_entry;
54 GtkLabel *local_password_hint;
55 GtkCheckButton *local_password_radio;
56 GtkEntry *local_username_entry;
57 AdwActionRow *local_username_row;
58 GtkImage *local_username_status_icon;
59 GtkPasswordEntry *local_verify_entry;
60 AdwActionRow *local_verify_password_row;
61 GtkImage *local_verify_status_icon;
62 AdwPreferencesGroup *password_group;
63 GtkSpinner *spinner;
64
65 GCancellable *cancellable;
66 GPermission *permission;
67 ActUser *user;
68
69 gboolean has_custom_username;
70 gint local_name_timeout_id;
71 gint local_username_timeout_id;
72 ActUserPasswordMode local_password_mode;
73 gint local_password_timeout_id;
74 gboolean local_valid_username;
75 };
76
77 G_DEFINE_TYPE (CcAddUserDialog, cc_add_user_dialog, ADW_TYPE_DIALOG);
78
79 static void
80 show_error_dialog (CcAddUserDialog *self,
81 const gchar *message,
82 GError *error)
83 {
84 GtkWidget *dialog;
85
86 dialog = gtk_message_dialog_new (GTK_WINDOW (self),
87 GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT | GTK_DIALOG_USE_HEADER_BAR,
88 GTK_MESSAGE_ERROR,
89 GTK_BUTTONS_CLOSE,
90 "%s", message);
91
92 if (error != NULL) {
93 g_dbus_error_strip_remote_error (error);
94 gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
95 "%s", error->message);
96 }
97
98 g_signal_connect (dialog, "response", G_CALLBACK (gtk_window_destroy), NULL);
99 gtk_window_present (GTK_WINDOW (dialog));
100 }
101
102 static void
103 begin_action (CcAddUserDialog *self)
104 {
105 g_debug ("Beginning action, disabling dialog controls");
106
107 gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), FALSE);
108
109 gtk_widget_set_visible (GTK_WIDGET (self->spinner), TRUE);
110 gtk_spinner_start (self->spinner);
111 }
112
113 static void
114 finish_action (CcAddUserDialog *self)
115 {
116 g_debug ("Completed domain action");
117
118 gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), TRUE);
119
120 gtk_widget_set_visible (GTK_WIDGET (self->spinner), FALSE);
121 gtk_spinner_stop (self->spinner);
122 }
123
124 static void
125 user_loaded_cb (CcAddUserDialog *self,
126 GParamSpec *pspec,
127 ActUser *user)
128 {
129 const gchar *password;
130
131 finish_action (self);
132
133 /* Set a password for the user */
134 password = gtk_editable_get_text (GTK_EDITABLE (self->local_password_entry));
135 act_user_set_password_mode (user, self->local_password_mode);
136 if (self->local_password_mode == ACT_USER_PASSWORD_MODE_REGULAR)
137 act_user_set_password (user, password, "");
138
139 self->user = g_object_ref (user);
140 adw_dialog_close (ADW_DIALOG (self));
141 }
142
143 static void
144 create_user_done (ActUserManager *manager,
145 GAsyncResult *res,
146 CcAddUserDialog *self)
147 {
148 ActUser *user;
149 g_autoptr(GError) error = NULL;
150
151 /* Note that user is returned without an extra reference */
152
153 user = act_user_manager_create_user_finish (manager, res, &error);
154
155 if (user == NULL) {
156 finish_action (self);
157 g_debug ("Failed to create user: %s", error->message);
158 if (!g_error_matches (error, ACT_USER_MANAGER_ERROR, ACT_USER_MANAGER_ERROR_PERMISSION_DENIED))
159 show_error_dialog (self, _("Failed to add account"), error);
160 gtk_widget_grab_focus (GTK_WIDGET (self->local_name_entry));
161 } else {
162 g_debug ("Created user: %s", act_user_get_user_name (user));
163
164 /* Check if the returned object is fully loaded before returning it */
165 if (act_user_is_loaded (user))
166 user_loaded_cb (self, NULL, user);
167 else
168 g_signal_connect_object (user, "notify::is-loaded", G_CALLBACK (user_loaded_cb), self, G_CONNECT_SWAPPED);
169 }
170 }
171
172 static void
173 local_create_user (CcAddUserDialog *self)
174 {
175 ActUserManager *manager;
176 const gchar *username;
177 const gchar *name;
178 gint account_type;
179
180 begin_action (self);
181
182 name = gtk_editable_get_text (GTK_EDITABLE (self->local_name_entry));
183 username = gtk_combo_box_text_get_active_text (self->local_username_combo);
184 account_type = gtk_switch_get_active (self->local_account_type_switch) ? ACT_USER_ACCOUNT_TYPE_ADMINISTRATOR : ACT_USER_ACCOUNT_TYPE_STANDARD;
185
186 g_debug ("Creating local user: %s", username);
187
188 manager = act_user_manager_get_default ();
189 act_user_manager_create_user_async (manager,
190 username,
191 name,
192 account_type,
193 self->cancellable,
194 (GAsyncReadyCallback)create_user_done,
195 self);
196 }
197
198 static gint
199 update_password_strength (CcAddUserDialog *self)
200 {
201 const gchar *password;
202 const gchar *username;
203 const gchar *hint;
204 const gchar *verify;
205 gint strength_level;
206
207 password = gtk_editable_get_text (GTK_EDITABLE (self->local_password_entry));
208 username = gtk_combo_box_text_get_active_text (self->local_username_combo);
209
210 pw_strength (password, NULL, username, &hint, &strength_level);
211
212 gtk_level_bar_set_value (self->local_strength_indicator, strength_level);
213 gtk_label_set_label (self->local_password_hint, hint);
214
215 if (strength_level > 1) {
216 gtk_image_set_from_icon_name (self->local_password_status_icon, "emblem-ok-symbolic");
217 } else if (strlen (password) == 0) {
218 gtk_image_set_from_icon_name (self->local_password_status_icon, "dialog-warning-symbolic");
219 } else {
220 gtk_image_set_from_icon_name (self->local_password_status_icon, "dialog-warning-symbolic");
221 }
222
223 verify = gtk_editable_get_text (GTK_EDITABLE (self->local_verify_entry));
224 if (strlen (verify) == 0) {
225 gtk_widget_set_sensitive (GTK_WIDGET (self->local_verify_entry), strength_level > 1);
226 }
227
228 return strength_level;
229 }
230
231 static gboolean
232 local_validate (CcAddUserDialog *self)
233 {
234 gboolean valid_name;
235 gboolean valid_password;
236 const gchar *name;
237 const gchar *password;
238 const gchar *verify;
239 gint strength;
240
241 if (self->local_valid_username) {
242 gtk_image_set_from_icon_name (self->local_username_status_icon, "emblem-ok-symbolic");
243 }
244
245 name = gtk_editable_get_text (GTK_EDITABLE (self->local_name_entry));
246 valid_name = is_valid_name (name);
247 if (valid_name) {
248 gtk_image_set_from_icon_name (self->local_name_status_icon, "emblem-ok-symbolic");
249 }
250
251 password = gtk_editable_get_text (GTK_EDITABLE (self->local_password_entry));
252 verify = gtk_editable_get_text (GTK_EDITABLE (self->local_verify_entry));
253 if (self->local_password_mode == ACT_USER_PASSWORD_MODE_REGULAR) {
254 strength = update_password_strength (self);
255 valid_password = strength > 1 && strcmp (password, verify) == 0;
256 } else {
257 valid_password = TRUE;
258 }
259
260 return valid_name && self->local_valid_username && valid_password;
261 }
262
263 static void local_username_is_valid_cb (GObject *source_object,
264 GAsyncResult *result,
265 gpointer user_data)
266 {
267 g_autoptr(CcAddUserDialog) self = CC_ADD_USER_DIALOG (user_data);
268 g_autoptr(GError) error = NULL;
269 g_autofree gchar *tip = NULL;
270 g_autofree gchar *name = NULL;
271 g_autofree gchar *username = NULL;
272 gboolean valid;
273
274 valid = is_valid_username_finish (result, &tip, &username, &error);
275 if (error != NULL) {
276 g_warning ("Could not check username by usermod: %s", error->message);
277 valid = TRUE;
278 }
279
280 name = gtk_combo_box_text_get_active_text (self->local_username_combo);
281 if (g_strcmp0 (name, username) == 0) {
282 self->local_valid_username = valid;
283 adw_action_row_set_subtitle (ADW_ACTION_ROW (self->local_username_row), tip);
284 dialog_validate (self);
285 }
286 }
287
288 static gboolean
289 local_username_timeout (CcAddUserDialog *self)
290 {
291 g_autofree gchar *name = NULL;
292
293 self->local_username_timeout_id = 0;
294
295 name = gtk_combo_box_text_get_active_text (self->local_username_combo);
296 is_valid_username_async (name, NULL, local_username_is_valid_cb, g_object_ref (self));
297
298 return FALSE;
299 }
300
301 static gboolean
302 local_username_combo_focus_out_event_cb (CcAddUserDialog *self)
303 {
304 g_clear_handle_id (&self->local_username_timeout_id, g_source_remove);
305
306 local_username_timeout (self);
307
308 return FALSE;
309 }
310
311 static void
312 local_username_combo_changed_cb (CcAddUserDialog *self)
313 {
314 const gchar *username;
315
316 username = gtk_editable_get_text (GTK_EDITABLE (self->local_username_entry));
317 if (*username == '\0')
318 self->has_custom_username = FALSE;
319 else if (gtk_widget_has_focus (GTK_WIDGET (self->local_username_entry)) ||
320 gtk_combo_box_get_active (GTK_COMBO_BOX (self->local_username_combo)) > 0)
321 self->has_custom_username = TRUE;
322
323 g_clear_handle_id (&self->local_username_timeout_id, g_source_remove);
324
325 gtk_image_set_from_icon_name (self->local_username_status_icon, "dialog-warning-symbolic");
326 gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), FALSE);
327
328 self->local_valid_username = FALSE;
329 self->local_username_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT, (GSourceFunc) local_username_timeout, self);
330 }
331
332 static gboolean
333 local_name_timeout (CcAddUserDialog *self)
334 {
335 self->local_name_timeout_id = 0;
336
337 dialog_validate (self);
338
339 return FALSE;
340 }
341
342 static gboolean
343 local_name_entry_focus_out_event_cb (CcAddUserDialog *self)
344 {
345 g_clear_handle_id (&self->local_name_timeout_id, g_source_remove);
346
347 local_name_timeout (self);
348
349 return FALSE;
350 }
351
352 static void
353 generate_username_choices (const gchar *name,
354 GtkListStore *store)
355 {
356 gboolean in_use, same_as_initial;
357 g_autofree gchar *lc_name = NULL;
358 g_autofree gchar *ascii_name = NULL;
359 g_autofree gchar *stripped_name = NULL;
360 g_auto(GStrv) words1 = NULL;
361 char **w1, **w2;
362 char *c;
363 char *unicode_fallback = "?";
364 g_autoptr(GString) first_word = NULL;
365 g_autoptr(GString) last_word = NULL;
366 g_autoptr(GString) item0 = NULL;
367 g_autoptr(GString) item1 = NULL;
368 g_autoptr(GString) item2 = NULL;
369 g_autoptr(GString) item3 = NULL;
370 g_autoptr(GString) item4 = NULL;
371 int len;
372 int nwords1, nwords2, i;
373 g_autoptr(GHashTable) items = NULL;
374 GtkTreeIter iter;
375 gsize max_name_length;
376
377 gtk_list_store_clear (store);
378
379 ascii_name = g_convert_with_fallback (name, -1, "ASCII//TRANSLIT", "UTF-8",
380 unicode_fallback, NULL, NULL, NULL);
381 /* Re-try without TRANSLIT. musl does not implement it */
382 if (ascii_name == NULL)
383 ascii_name = g_convert_with_fallback (name, -1, "ASCII", "UTF-8",
384 unicode_fallback, NULL, NULL, NULL);
385 if (ascii_name == NULL)
386 return;
387
388 lc_name = g_ascii_strdown (ascii_name, -1);
389
390 /* Remove all non ASCII alphanumeric chars from the name,
391 * apart from the few allowed symbols.
392 *
393 * We do remove '.', even though it is usually allowed,
394 * since it often comes in via an abbreviated middle name,
395 * and the dot looks just wrong in the proposals then.
396 */
397 stripped_name = g_strnfill (strlen (lc_name) + 1, '\0');
398 i = 0;
399 for (c = lc_name; *c; c++) {
400 if (!(g_ascii_isdigit (*c) || g_ascii_islower (*c) ||
401 *c == ' ' || *c == '-' || *c == '_' ||
402 /* used to track invalid words, removed below */
403 *c == '?') )
404 continue;
405
406 stripped_name[i] = *c;
407 i++;
408 }
409
410 if (strlen (stripped_name) == 0) {
411 return;
412 }
413
414 /* we split name on spaces, and then on dashes, so that we can treat
415 * words linked with dashes the same way, i.e. both fully shown, or
416 * both abbreviated
417 */
418 words1 = g_strsplit_set (stripped_name, " ", -1);
419 len = g_strv_length (words1);
420
421 /* The default item is a concatenation of all words without ? */
422 item0 = g_string_sized_new (strlen (stripped_name));
423
424 /* Concatenate the whole first word with the first letter of each
425 * word (item1), and the last word with the first letter of each
426 * word (item2). item3 and item4 are symmetrical respectively to
427 * item1 and item2.
428 *
429 * Constant 5 is the max reasonable number of words we may get when
430 * splitting on dashes, since we can't guess it at this point,
431 * and reallocating would be too bad.
432 */
433 item1 = g_string_sized_new (strlen (words1[0]) + len - 1 + 5);
434 item3 = g_string_sized_new (strlen (words1[0]) + len - 1 + 5);
435
436 item2 = g_string_sized_new (strlen (words1[len - 1]) + len - 1 + 5);
437 item4 = g_string_sized_new (strlen (words1[len - 1]) + len - 1 + 5);
438
439 /* again, guess at the max size of names */
440 first_word = g_string_sized_new (20);
441 last_word = g_string_sized_new (20);
442
443 nwords1 = 0;
444 nwords2 = 0;
445 for (w1 = words1; *w1; w1++) {
446 g_auto(GStrv) words2 = NULL;
447
448 if (strlen (*w1) == 0)
449 continue;
450
451 /* skip words with string '?', most likely resulting
452 * from failed transliteration to ASCII
453 */
454 if (strstr (*w1, unicode_fallback) != NULL)
455 continue;
456
457 nwords1++; /* count real words, excluding empty string */
458
459 item0 = g_string_append (item0, *w1);
460
461 words2 = g_strsplit_set (*w1, "-", -1);
462 /* reset last word if a new non-empty word has been found */
463 if (strlen (*words2) > 0)
464 last_word = g_string_set_size (last_word, 0);
465
466 for (w2 = words2; *w2; w2++) {
467 if (strlen (*w2) == 0)
468 continue;
469
470 nwords2++;
471
472 /* part of the first "toplevel" real word */
473 if (nwords1 == 1) {
474 item1 = g_string_append (item1, *w2);
475 first_word = g_string_append (first_word, *w2);
476 }
477 else {
478 item1 = g_string_append_unichar (item1,
479 g_utf8_get_char (*w2));
480 item3 = g_string_append_unichar (item3,
481 g_utf8_get_char (*w2));
482 }
483
484 /* not part of the last "toplevel" word */
485 if (w1 != words1 + len - 1) {
486 item2 = g_string_append_unichar (item2,
487 g_utf8_get_char (*w2));
488 item4 = g_string_append_unichar (item4,
489 g_utf8_get_char (*w2));
490 }
491
492 /* always save current word so that we have it if last one reveals empty */
493 last_word = g_string_append (last_word, *w2);
494 }
495 }
496 item2 = g_string_append (item2, last_word->str);
497 item3 = g_string_append (item3, first_word->str);
498 item4 = g_string_prepend (item4, last_word->str);
499
500 max_name_length = get_username_max_length ();
501
502 g_string_truncate (first_word, max_name_length);
503 g_string_truncate (last_word, max_name_length);
504
505 g_string_truncate (item0, max_name_length);
506 g_string_truncate (item1, max_name_length);
507 g_string_truncate (item2, max_name_length);
508 g_string_truncate (item3, max_name_length);
509 g_string_truncate (item4, max_name_length);
510
511 items = g_hash_table_new (g_str_hash, g_str_equal);
512
513 in_use = is_username_used (item0->str);
514 if (!in_use && !g_ascii_isdigit (item0->str[0])) {
515 gtk_list_store_append (store, &iter);
516 gtk_list_store_set (store, &iter, 0, item0->str, -1);
517 g_hash_table_insert (items, item0->str, item0->str);
518 }
519
520 in_use = is_username_used (item1->str);
521 same_as_initial = (g_strcmp0 (item0->str, item1->str) == 0);
522 if (!same_as_initial && nwords2 > 0 && !in_use && !g_ascii_isdigit (item1->str[0])) {
523 gtk_list_store_append (store, &iter);
524 gtk_list_store_set (store, &iter, 0, item1->str, -1);
525 g_hash_table_insert (items, item1->str, item1->str);
526 }
527
528 /* if there's only one word, would be the same as item1 */
529 if (nwords2 > 1) {
530 /* add other items */
531 in_use = is_username_used (item2->str);
532 if (!in_use && !g_ascii_isdigit (item2->str[0]) &&
533 !g_hash_table_lookup (items, item2->str)) {
534 gtk_list_store_append (store, &iter);
535 gtk_list_store_set (store, &iter, 0, item2->str, -1);
536 g_hash_table_insert (items, item2->str, item2->str);
537 }
538
539 in_use = is_username_used (item3->str);
540 if (!in_use && !g_ascii_isdigit (item3->str[0]) &&
541 !g_hash_table_lookup (items, item3->str)) {
542 gtk_list_store_append (store, &iter);
543 gtk_list_store_set (store, &iter, 0, item3->str, -1);
544 g_hash_table_insert (items, item3->str, item3->str);
545 }
546
547 in_use = is_username_used (item4->str);
548 if (!in_use && !g_ascii_isdigit (item4->str[0]) &&
549 !g_hash_table_lookup (items, item4->str)) {
550 gtk_list_store_append (store, &iter);
551 gtk_list_store_set (store, &iter, 0, item4->str, -1);
552 g_hash_table_insert (items, item4->str, item4->str);
553 }
554
555 /* add the last word */
556 in_use = is_username_used (last_word->str);
557 if (!in_use && !g_ascii_isdigit (last_word->str[0]) &&
558 !g_hash_table_lookup (items, last_word->str)) {
559 gtk_list_store_append (store, &iter);
560 gtk_list_store_set (store, &iter, 0, last_word->str, -1);
561 g_hash_table_insert (items, last_word->str, last_word->str);
562 }
563
564 /* ...and the first one */
565 in_use = is_username_used (first_word->str);
566 if (!in_use && !g_ascii_isdigit (first_word->str[0]) &&
567 !g_hash_table_lookup (items, first_word->str)) {
568 gtk_list_store_append (store, &iter);
569 gtk_list_store_set (store, &iter, 0, first_word->str, -1);
570 g_hash_table_insert (items, first_word->str, first_word->str);
571 }
572 }
573 }
574
575 static void
576 local_name_entry_changed_cb (CcAddUserDialog *self)
577 {
578 const char *name;
579
580 gtk_list_store_clear (self->local_username_model);
581
582 name = gtk_editable_get_text (GTK_EDITABLE (self->local_name_entry));
583 if ((name == NULL || strlen (name) == 0) && !self->has_custom_username) {
584 gtk_editable_set_text (GTK_EDITABLE (self->local_username_entry), "");
585 } else if (name != NULL && strlen (name) != 0) {
586 generate_username_choices (name, self->local_username_model);
587 if (!self->has_custom_username)
588 gtk_combo_box_set_active (GTK_COMBO_BOX (self->local_username_combo), 0);
589 }
590
591 g_clear_handle_id (&self->local_name_timeout_id, g_source_remove);
592
593 gtk_image_set_from_icon_name (self->local_name_status_icon, "dialog-warning-symbolic");
594 gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), FALSE);
595
596 self->local_name_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT, (GSourceFunc) local_name_timeout, self);
597 }
598
599 static void
600 update_password_match (CcAddUserDialog *self)
601 {
602 const gchar *password;
603 const gchar *verify;
604 const gchar *message = "";
605
606 password = gtk_editable_get_text (GTK_EDITABLE (self->local_password_entry));
607 verify = gtk_editable_get_text (GTK_EDITABLE (self->local_verify_entry));
608 if (strlen (verify) != 0) {
609 if (strcmp (password, verify) != 0) {
610 message = _("The passwords do not match.");
611 } else {
612 gtk_image_set_from_icon_name (self->local_verify_status_icon, "emblem-ok-symbolic");
613 }
614 }
615 adw_action_row_set_subtitle (ADW_ACTION_ROW (self->local_verify_password_row), message);
616 }
617
618 static void
619 generate_password (CcAddUserDialog *self)
620 {
621 g_autofree gchar *pwd = NULL;
622
623 pwd = pw_generate ();
624 if (pwd == NULL)
625 return;
626
627 gtk_editable_set_text (GTK_EDITABLE (self->local_password_entry), pwd);
628 gtk_editable_set_text (GTK_EDITABLE (self->local_verify_entry), pwd);
629 gtk_widget_set_sensitive (GTK_WIDGET (self->local_verify_entry), TRUE);
630 }
631
632 static gboolean
633 local_password_timeout (CcAddUserDialog *self)
634 {
635 self->local_password_timeout_id = 0;
636
637 dialog_validate (self);
638 update_password_match (self);
639
640 return FALSE;
641 }
642
643 static gboolean
644 password_focus_out_event_cb (CcAddUserDialog *self)
645 {
646 g_clear_handle_id (&self->local_password_timeout_id, g_source_remove);
647
648 local_password_timeout (self);
649
650 return FALSE;
651 }
652
653 static gboolean
654 local_password_entry_key_press_event_cb (CcAddUserDialog *self,
655 guint keyval,
656 guint keycode,
657 GdkModifierType state,
658 GtkEventControllerKey *controller)
659 {
660 if (keyval == GDK_KEY_Tab)
661 local_password_timeout (self);
662
663 return FALSE;
664 }
665
666 static void
667 recheck_password_match (CcAddUserDialog *self)
668 {
669 g_clear_handle_id (&self->local_password_timeout_id, g_source_remove);
670
671 gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), FALSE);
672
673 self->local_password_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT, (GSourceFunc) local_password_timeout, self);
674 }
675
676 static void
677 local_password_entry_changed_cb (CcAddUserDialog *self)
678 {
679 gtk_image_set_from_icon_name (self->local_password_status_icon, "dialog-warning-symbolic");
680 gtk_image_set_from_icon_name (self->local_verify_status_icon, "dialog-warning-symbolic");
681 recheck_password_match (self);
682 }
683
684 static void
685 local_verify_entry_changed_cb (CcAddUserDialog *self)
686 {
687 gtk_image_set_from_icon_name (self->local_verify_status_icon, "dialog-warning-symbolic");
688 recheck_password_match (self);
689 }
690
691 static void
692 local_password_radio_changed_cb (CcAddUserDialog *self)
693 {
694 gboolean active;
695
696 active = gtk_check_button_get_active (GTK_CHECK_BUTTON (self->local_password_radio));
697 self->local_password_mode = active ? ACT_USER_PASSWORD_MODE_REGULAR : ACT_USER_PASSWORD_MODE_SET_AT_LOGIN;
698
699 gtk_widget_set_sensitive (GTK_WIDGET (self->password_group), active);
700
701 dialog_validate (self);
702 }
703
704 static void
705 dialog_validate (CcAddUserDialog *self)
706 {
707 gboolean valid = FALSE;
708
709 valid = local_validate (self);
710
711 gtk_widget_set_sensitive (GTK_WIDGET (self->add_button), valid);
712 }
713
714 static void
715 cc_add_user_dialog_init (CcAddUserDialog *self)
716 {
717 gtk_widget_init_template (GTK_WIDGET (self));
718
719 self->cancellable = g_cancellable_new ();
720
721 self->local_password_mode = ACT_USER_PASSWORD_MODE_SET_AT_LOGIN;
722 dialog_validate (self);
723 update_password_strength (self);
724 local_username_timeout (self);
725 }
726
727 static void
728 on_permission_acquired (GObject *source_object,
729 GAsyncResult *res,
730 gpointer user_data)
731 {
732 g_autoptr(CcAddUserDialog) self = CC_ADD_USER_DIALOG (user_data);
733 g_autoptr(GError) error = NULL;
734
735 /* Paired with begin_action in cc_add_user_dialog_response () */
736 finish_action (self);
737
738 if (g_permission_acquire_finish (self->permission, res, &error)) {
739 g_return_if_fail (g_permission_get_allowed (self->permission));
740 add_button_clicked_cb (self);
741 } else if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
742 g_warning ("Failed to acquire permission: %s", error->message);
743 }
744 }
745
746 static void
747 add_button_clicked_cb (CcAddUserDialog *self)
748 {
749 /* We don't (or no longer) have necessary permissions */
750 if (self->permission && !g_permission_get_allowed (self->permission)) {
751 begin_action (self);
752 g_permission_acquire_async (self->permission, self->cancellable,
753 on_permission_acquired, g_object_ref (self));
754 return;
755 }
756
757 local_create_user (self);
758 }
759
760 static void
761 cc_add_user_dialog_dispose (GObject *obj)
762 {
763 CcAddUserDialog *self = CC_ADD_USER_DIALOG (obj);
764
765 if (self->cancellable)
766 g_cancellable_cancel (self->cancellable);
767
768 g_clear_object (&self->user);
769
770 g_clear_handle_id (&self->local_password_timeout_id, g_source_remove);
771 g_clear_handle_id (&self->local_name_timeout_id, g_source_remove);
772 g_clear_handle_id (&self->local_username_timeout_id, g_source_remove);
773
774 G_OBJECT_CLASS (cc_add_user_dialog_parent_class)->dispose (obj);
775 }
776
777 static void
778 cc_add_user_dialog_finalize (GObject *obj)
779 {
780 CcAddUserDialog *self = CC_ADD_USER_DIALOG (obj);
781
782 g_clear_object (&self->cancellable);
783 g_clear_object (&self->permission);
784
785 G_OBJECT_CLASS (cc_add_user_dialog_parent_class)->finalize (obj);
786 }
787
788 static void
789 cc_add_user_dialog_class_init (CcAddUserDialogClass *klass)
790 {
791 GObjectClass *object_class = G_OBJECT_CLASS (klass);
792 GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass);
793
794 object_class->dispose = cc_add_user_dialog_dispose;
795 object_class->finalize = cc_add_user_dialog_finalize;
796
797 gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/system/users/cc-add-user-dialog.ui");
798
799 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, add_button);
800 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_account_type_switch);
801 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_page);
802 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_password_hint);
803 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_password_row);
804 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_password_status_icon);
805 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_name_entry);
806 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_name_status_icon);
807 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_username_combo);
808 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_username_model);
809 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_password_entry);
810 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_password_radio);
811 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_username_entry);
812 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_username_row);
813 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_username_status_icon);
814 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_strength_indicator);
815 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_verify_entry);
816 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_verify_password_row);
817 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, local_verify_status_icon);
818 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, password_group);
819 gtk_widget_class_bind_template_child (widget_class, CcAddUserDialog, spinner);
820
821 gtk_widget_class_bind_template_callback (widget_class, add_button_clicked_cb);
822 gtk_widget_class_bind_template_callback (widget_class, dialog_validate);
823 gtk_widget_class_bind_template_callback (widget_class, generate_password);
824 gtk_widget_class_bind_template_callback (widget_class, local_name_entry_changed_cb);
825 gtk_widget_class_bind_template_callback (widget_class, local_name_entry_focus_out_event_cb);
826 gtk_widget_class_bind_template_callback (widget_class, local_password_entry_changed_cb);
827 gtk_widget_class_bind_template_callback (widget_class, local_password_entry_key_press_event_cb);
828 gtk_widget_class_bind_template_callback (widget_class, local_password_radio_changed_cb);
829 gtk_widget_class_bind_template_callback (widget_class, local_username_combo_changed_cb);
830 gtk_widget_class_bind_template_callback (widget_class, local_username_combo_focus_out_event_cb);
831 gtk_widget_class_bind_template_callback (widget_class, local_verify_entry_changed_cb);
832 gtk_widget_class_bind_template_callback (widget_class, password_focus_out_event_cb);
833 }
834
835 CcAddUserDialog *
836 cc_add_user_dialog_new (GPermission *permission)
837 {
838 CcAddUserDialog *self;
839
840 self = g_object_new (CC_TYPE_ADD_USER_DIALOG, NULL);
841
842 if (permission != NULL)
843 self->permission = g_object_ref (permission);
844
845 return self;
846 }
847
848 ActUser *
849 cc_add_user_dialog_get_user (CcAddUserDialog *self)
850 {
851 g_return_val_if_fail (CC_IS_ADD_USER_DIALOG (self), NULL);
852 return self->user;
853 }
854