/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- * * Copyright 2009-2010 Red Hat, Inc, * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . * * Written by: Matthias Clasen */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "cc-password-dialog.h" #include "cc-user-accounts-resources.h" #include "pw-utils.h" #include "run-passwd.h" #include "user-utils.h" #define PASSWORD_CHECK_TIMEOUT 600 struct _CcPasswordDialog { AdwWindow parent_instance; GtkCheckButton *action_login_radio; GtkCheckButton *action_now_radio; GtkButton *generate_password_button; GtkButton *ok_button; AdwPasswordEntryRow *old_password_entry; AdwPreferencesGroup *password_group; AdwPreferencesGroup *password_on_next_login_group; AdwPasswordEntryRow *password_entry; GtkLabel *password_hint_label; GtkLevelBar *strength_indicator; AdwPasswordEntryRow *verify_entry; GtkLabel *verify_label; gint password_entry_timeout_id; ActUser *user; ActUserPasswordMode password_mode; gboolean old_password_ok; gint old_password_entry_timeout_id; PasswdHandler *passwd_handler; }; G_DEFINE_TYPE (CcPasswordDialog, cc_password_dialog, ADW_TYPE_WINDOW) static gint update_password_strength (CcPasswordDialog *self) { const gchar *password; const gchar *old_password; const gchar *username; gint strength_level; const gchar *hint; const gchar *verify; password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry)); old_password = gtk_editable_get_text (GTK_EDITABLE (self->old_password_entry)); username = act_user_get_user_name (self->user); pw_strength (password, old_password, username, &hint, &strength_level); gtk_level_bar_set_value (self->strength_indicator, strength_level); gtk_label_set_label (self->password_hint_label, hint); if (strength_level > 1) { gtk_widget_remove_css_class (GTK_WIDGET (self->password_entry), "error"); } else if (strlen (password) == 0) { //gtk_widget_hide (GTK_WIDGET (self->password_entry_status_icon)); //gtk_widget_show (GTK_WIDGET (self->generate_password_button)); } else { gtk_widget_add_css_class (GTK_WIDGET (self->password_entry), "error"); } verify = gtk_editable_get_text (GTK_EDITABLE (self->verify_entry)); if (strlen (verify) == 0) { gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), strength_level > 1); } return strength_level; } static void password_changed_cb (PasswdHandler *handler, GError *error, CcPasswordDialog *self) { GtkWidget *dialog; const gchar *primary_text; const gchar *secondary_text; gtk_widget_set_sensitive (GTK_WIDGET (self), TRUE); if (!error) { gtk_window_close (GTK_WINDOW (self)); return; } if (error->code == PASSWD_ERROR_REJECTED) { primary_text = error->message; secondary_text = _("Please choose another password."); gtk_editable_set_text (GTK_EDITABLE (self->password_entry), ""); gtk_widget_grab_focus (GTK_WIDGET (self->password_entry)); gtk_editable_set_text (GTK_EDITABLE (self->verify_entry), ""); } else if (error->code == PASSWD_ERROR_AUTH_FAILED) { primary_text = error->message; secondary_text = _("Please type your current password again."); gtk_editable_set_text (GTK_EDITABLE (self->old_password_entry), ""); gtk_widget_grab_focus (GTK_WIDGET (self->old_password_entry)); } else { primary_text = _("Password could not be changed"); secondary_text = error->message; } dialog = gtk_message_dialog_new (GTK_WINDOW (self), GTK_DIALOG_MODAL, GTK_MESSAGE_ERROR, GTK_BUTTONS_CLOSE, "%s", primary_text); gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog), "%s", secondary_text); gtk_window_present (GTK_WINDOW (dialog)); } static void ok_button_clicked_cb (CcPasswordDialog *self) { const gchar *password; password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry)); switch (self->password_mode) { case ACT_USER_PASSWORD_MODE_REGULAR: if (act_user_get_uid (self->user) == getuid ()) { /* When setting a password for the current user, * use passwd directly, to preserve the audit trail * and to e.g. update the keyring password. */ passwd_change_password (self->passwd_handler, password, (PasswdCallback) password_changed_cb, self); gtk_widget_set_sensitive (GTK_WIDGET (self), FALSE); return; } act_user_set_password_mode (self->user, ACT_USER_PASSWORD_MODE_REGULAR); act_user_set_password (self->user, password, ""); break; case ACT_USER_PASSWORD_MODE_SET_AT_LOGIN: act_user_set_password_mode (self->user, self->password_mode); act_user_set_automatic_login (self->user, FALSE); break; default: g_assert_not_reached (); } gtk_window_close (GTK_WINDOW (self)); } static void update_sensitivity (CcPasswordDialog *self) { const gchar *password, *verify; gboolean can_change; int strength; password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry)); verify = gtk_editable_get_text (GTK_EDITABLE (self->verify_entry)); if (self->password_mode == ACT_USER_PASSWORD_MODE_REGULAR) { strength = update_password_strength (self); can_change = strength > 1 && strcmp (password, verify) == 0 && (self->old_password_ok || !gtk_widget_get_visible (GTK_WIDGET (self->old_password_entry))); } else { can_change = TRUE; } gtk_widget_set_sensitive (GTK_WIDGET (self->ok_button), can_change); } static void mode_change (CcPasswordDialog *self, ActUserPasswordMode mode) { gboolean active; active = (mode == ACT_USER_PASSWORD_MODE_REGULAR); gtk_widget_set_sensitive (GTK_WIDGET (self->password_entry), active); gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), active); gtk_widget_set_sensitive (GTK_WIDGET (self->old_password_entry), active); gtk_check_button_set_active (GTK_CHECK_BUTTON (self->action_now_radio), active); gtk_check_button_set_active (GTK_CHECK_BUTTON (self->action_login_radio), !active); self->password_mode = mode; update_sensitivity (self); } static void action_now_radio_toggled_cb (CcPasswordDialog *self) { gint active; ActUserPasswordMode mode; active = gtk_check_button_get_active (GTK_CHECK_BUTTON (self->action_now_radio)); mode = active ? ACT_USER_PASSWORD_MODE_REGULAR : ACT_USER_PASSWORD_MODE_SET_AT_LOGIN; mode_change (self, mode); } static void update_password_match (CcPasswordDialog *self) { const gchar *password; const gchar *verify; password = gtk_editable_get_text (GTK_EDITABLE (self->password_entry)); verify = gtk_editable_get_text (GTK_EDITABLE (self->verify_entry)); if (strlen (verify) > 0) { if (strcmp (password, verify) != 0) { gtk_widget_set_visible (GTK_WIDGET (self->verify_label), TRUE); gtk_widget_add_css_class (GTK_WIDGET (self->verify_entry), "error"); } else { gtk_widget_set_visible (GTK_WIDGET (self->verify_label), FALSE); gtk_widget_remove_css_class (GTK_WIDGET (self->verify_entry), "error"); } } } static gboolean password_entry_timeout (CcPasswordDialog *self) { update_password_strength (self); update_sensitivity (self); update_password_match (self); self->password_entry_timeout_id = 0; return FALSE; } static void recheck_password_match (CcPasswordDialog *self) { if (self->password_entry_timeout_id != 0) { g_source_remove (self->password_entry_timeout_id); self->password_entry_timeout_id = 0; } gtk_widget_set_sensitive (GTK_WIDGET (self->ok_button), FALSE); self->password_entry_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT, (GSourceFunc) password_entry_timeout, self); } static void password_entry_changed (CcPasswordDialog *self) { gtk_widget_add_css_class (GTK_WIDGET (self->password_entry), "error"); gtk_widget_add_css_class (GTK_WIDGET (self->verify_entry), "error"); recheck_password_match (self); } static void verify_entry_changed (CcPasswordDialog *self) { gtk_widget_add_css_class (GTK_WIDGET (self->verify_entry), "error"); recheck_password_match (self); } static gboolean password_entry_focus_out_cb (CcPasswordDialog *self) { if (self->password_entry_timeout_id != 0) { g_source_remove (self->password_entry_timeout_id); self->password_entry_timeout_id = 0; } if (self->user != NULL) password_entry_timeout (self); return FALSE; } static gboolean password_entry_key_press_cb (GtkEventControllerKey *controller, guint keyval, guint keycode, GdkModifierType state, CcPasswordDialog *self) { if (self->password_entry_timeout_id != 0) { g_source_remove (self->password_entry_timeout_id); self->password_entry_timeout_id = 0; } if (keyval == GDK_KEY_Tab) password_entry_timeout (self); return FALSE; } static void auth_cb (PasswdHandler *handler, GError *error, CcPasswordDialog *self) { if (error) { self->old_password_ok = FALSE; } else { self->old_password_ok = TRUE; gtk_widget_remove_css_class (GTK_WIDGET (self->old_password_entry), "error"); } update_sensitivity (self); } static gboolean old_password_entry_timeout (CcPasswordDialog *self) { const gchar *text; update_sensitivity (self); text = gtk_editable_get_text (GTK_EDITABLE (self->old_password_entry)); if (!self->old_password_ok) { passwd_authenticate (self->passwd_handler, text, (PasswdCallback)auth_cb, self); } self->old_password_entry_timeout_id = 0; return FALSE; } static gboolean old_password_entry_focus_out_cb (CcPasswordDialog *self) { if (self->old_password_entry_timeout_id != 0) { g_source_remove (self->old_password_entry_timeout_id); self->old_password_entry_timeout_id = 0; } if (self->user != NULL) old_password_entry_timeout (self); return FALSE; } static void old_password_entry_changed (CcPasswordDialog *self) { if (self->old_password_entry_timeout_id != 0) { g_source_remove (self->old_password_entry_timeout_id); self->old_password_entry_timeout_id = 0; } gtk_widget_add_css_class (GTK_WIDGET (self->old_password_entry), "error"); gtk_widget_set_sensitive (GTK_WIDGET (self->ok_button), FALSE); self->old_password_ok = FALSE; self->old_password_entry_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT, (GSourceFunc) old_password_entry_timeout, self); } static void generate_password (CcPasswordDialog *self) { g_autofree gchar *pwd = NULL; pwd = pw_generate (); if (pwd == NULL) return; gtk_editable_set_text (GTK_EDITABLE (self->password_entry), pwd); gtk_editable_set_text (GTK_EDITABLE (self->verify_entry), pwd); gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), TRUE); gtk_widget_hide (GTK_WIDGET (self->generate_password_button)); } static void cc_password_dialog_dispose (GObject *object) { CcPasswordDialog *self = CC_PASSWORD_DIALOG (object); g_clear_object (&self->user); if (self->passwd_handler) { passwd_destroy (self->passwd_handler); self->passwd_handler = NULL; } if (self->old_password_entry_timeout_id != 0) { g_source_remove (self->old_password_entry_timeout_id); self->old_password_entry_timeout_id = 0; } if (self->password_entry_timeout_id != 0) { g_source_remove (self->password_entry_timeout_id); self->password_entry_timeout_id = 0; } G_OBJECT_CLASS (cc_password_dialog_parent_class)->dispose (object); } static void cc_password_dialog_class_init (CcPasswordDialogClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); GtkWidgetClass *widget_class = GTK_WIDGET_CLASS (klass); object_class->dispose = cc_password_dialog_dispose; gtk_widget_class_add_binding_action (widget_class, GDK_KEY_Escape, 0, "window.close", NULL); gtk_widget_class_set_template_from_resource (widget_class, "/org/gnome/control-center/user-accounts/cc-password-dialog.ui"); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, action_login_radio); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, action_now_radio); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, generate_password_button); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, ok_button); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, old_password_entry); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_group); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_on_next_login_group); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_entry); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, password_hint_label); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, strength_indicator); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, verify_entry); gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, verify_label); gtk_widget_class_bind_template_callback (widget_class, action_now_radio_toggled_cb); gtk_widget_class_bind_template_callback (widget_class, generate_password); gtk_widget_class_bind_template_callback (widget_class, old_password_entry_changed); gtk_widget_class_bind_template_callback (widget_class, old_password_entry_focus_out_cb); gtk_widget_class_bind_template_callback (widget_class, ok_button_clicked_cb); gtk_widget_class_bind_template_callback (widget_class, password_entry_changed); gtk_widget_class_bind_template_callback (widget_class, password_entry_focus_out_cb); gtk_widget_class_bind_template_callback (widget_class, password_entry_key_press_cb); gtk_widget_class_bind_template_callback (widget_class, verify_entry_changed); } static void cc_password_dialog_init (CcPasswordDialog *self) { g_resources_register (cc_user_accounts_get_resource ()); gtk_widget_init_template (GTK_WIDGET (self)); } CcPasswordDialog * cc_password_dialog_new (ActUser *user) { CcPasswordDialog *self; GtkWindow *window; g_return_val_if_fail (ACT_IS_USER (user), NULL); self = g_object_new (CC_TYPE_PASSWORD_DIALOG, NULL); self->user = g_object_ref (user); if (act_user_get_uid (self->user) == getuid ()) { gboolean visible; mode_change (self, ACT_USER_PASSWORD_MODE_REGULAR); gtk_widget_hide (GTK_WIDGET (self->password_on_next_login_group)); visible = (act_user_get_password_mode (user) != ACT_USER_PASSWORD_MODE_NONE); gtk_widget_set_visible (GTK_WIDGET (self->old_password_entry), visible); self->old_password_ok = !visible; self->passwd_handler = passwd_init (); } else { mode_change (self, ACT_USER_PASSWORD_MODE_SET_AT_LOGIN); gtk_widget_show (GTK_WIDGET (self->password_on_next_login_group)); gtk_widget_hide (GTK_WIDGET (self->old_password_entry)); self->old_password_ok = TRUE; } if (self->old_password_ok == FALSE) gtk_widget_grab_focus (GTK_WIDGET (self->old_password_entry)); else gtk_widget_grab_focus (GTK_WIDGET (self->password_entry)); window = (GtkWindow *) gtk_widget_get_native (GTK_WIDGET (self)); gtk_window_set_default_widget (window, GTK_WIDGET (self->ok_button)); return self; }