summaryrefslogtreecommitdiffstats
path: root/panels/user-accounts/cc-password-dialog.c
diff options
context:
space:
mode:
Diffstat (limited to 'panels/user-accounts/cc-password-dialog.c')
-rw-r--r--panels/user-accounts/cc-password-dialog.c538
1 files changed, 538 insertions, 0 deletions
diff --git a/panels/user-accounts/cc-password-dialog.c b/panels/user-accounts/cc-password-dialog.c
new file mode 100644
index 0000000..b199999
--- /dev/null
+++ b/panels/user-accounts/cc-password-dialog.c
@@ -0,0 +1,538 @@
+/* -*- 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 <http://www.gnu.org/licenses/>.
+ *
+ * Written by: Matthias Clasen <mclasen@redhat.com>
+ */
+
+#include "config.h"
+
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+#include <act/act.h>
+
+#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
+{
+ GtkDialog parent_instance;
+
+ GtkBox *action_radio_box;
+ GtkRadioButton *action_now_radio;
+ GtkRadioButton *action_login_radio;
+ GtkButton *ok_button;
+ GtkLabel *old_password_label;
+ GtkEntry *old_password_entry;
+ GtkEntry *password_entry;
+ GtkLabel *password_hint_label;
+ GtkLevelBar *strength_indicator;
+ GtkEntry *verify_entry;
+ GtkLabel *verify_hint_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, GTK_TYPE_DIALOG)
+
+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_entry_get_text (self->password_entry);
+ old_password = gtk_entry_get_text (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) {
+ set_entry_validation_checkmark (self->password_entry);
+ } else if (strlen (password) == 0) {
+ set_entry_generation_icon (self->password_entry);
+ } else {
+ clear_entry_validation_error (self->password_entry);
+ }
+
+ verify = gtk_entry_get_text (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);
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (self)), NULL);
+
+ if (!error) {
+ gtk_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
+ return;
+ }
+
+ if (error->code == PASSWD_ERROR_REJECTED) {
+ primary_text = error->message;
+ secondary_text = _("Please choose another password.");
+
+ gtk_entry_set_text (self->password_entry, "");
+ gtk_widget_grab_focus (GTK_WIDGET (self->password_entry));
+
+ gtk_entry_set_text (self->verify_entry, "");
+ }
+ else if (error->code == PASSWD_ERROR_AUTH_FAILED) {
+ primary_text = error->message;
+ secondary_text = _("Please type your current password again.");
+
+ gtk_entry_set_text (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_dialog_run (GTK_DIALOG (dialog));
+ gtk_widget_destroy (dialog);
+}
+
+static void
+ok_button_clicked_cb (CcPasswordDialog *self)
+{
+ const gchar *password;
+
+ password = gtk_entry_get_text (self->password_entry);
+
+ switch (self->password_mode) {
+ case ACT_USER_PASSWORD_MODE_REGULAR:
+ if (act_user_get_uid (self->user) == getuid ()) {
+ GdkDisplay *display;
+ g_autoptr(GdkCursor) cursor = NULL;
+
+ /* 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);
+ display = gtk_widget_get_display (GTK_WIDGET (self));
+ cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
+ gdk_window_set_cursor (gtk_widget_get_window (GTK_WIDGET (self)), cursor);
+ gdk_display_flush (display);
+ 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_dialog_response (GTK_DIALOG (self), GTK_RESPONSE_ACCEPT);
+}
+
+static void
+update_sensitivity (CcPasswordDialog *self)
+{
+ const gchar *password, *verify;
+ gboolean can_change;
+ int strength;
+
+ password = gtk_entry_get_text (self->password_entry);
+ verify = gtk_entry_get_text (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_widget_set_sensitive (GTK_WIDGET (self->password_hint_label), active);
+ gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (self->action_now_radio), active);
+ gtk_toggle_button_set_active (GTK_TOGGLE_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_toggle_button_get_active (GTK_TOGGLE_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;
+ const gchar *message = "";
+
+ password = gtk_entry_get_text (self->password_entry);
+ verify = gtk_entry_get_text (self->verify_entry);
+
+ if (strlen (verify) > 0) {
+ if (strcmp (password, verify) != 0) {
+ message = _("The passwords do not match.");
+ }
+ else {
+ set_entry_validation_checkmark (self->verify_entry);
+ }
+ }
+ gtk_label_set_label (self->verify_hint_label, message);
+}
+
+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)
+{
+ const gchar *password;
+
+ 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);
+
+ password = gtk_entry_get_text (self->password_entry);
+ if (strlen (password) == 0) {
+ gtk_entry_set_visibility (self->password_entry, FALSE);
+ }
+
+ self->password_entry_timeout_id = g_timeout_add (PASSWORD_CHECK_TIMEOUT,
+ (GSourceFunc) password_entry_timeout,
+ self);
+}
+
+static void
+password_entry_changed (CcPasswordDialog *self)
+{
+ clear_entry_validation_error (self->password_entry);
+ clear_entry_validation_error (self->verify_entry);
+ recheck_password_match (self);
+}
+
+static void
+verify_entry_changed (CcPasswordDialog *self)
+{
+ clear_entry_validation_error (self->verify_entry);
+ 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 (CcPasswordDialog *self,
+ GdkEvent *event)
+{
+ GdkEventKey *key = (GdkEventKey *)event;
+
+ if (self->password_entry_timeout_id != 0) {
+ g_source_remove (self->password_entry_timeout_id);
+ self->password_entry_timeout_id = 0;
+ }
+
+ if (key->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;
+ set_entry_validation_checkmark (self->old_password_entry);
+ }
+
+ update_sensitivity (self);
+}
+
+static gboolean
+old_password_entry_timeout (CcPasswordDialog *self)
+{
+ const gchar *text;
+
+ update_sensitivity (self);
+
+ text = gtk_entry_get_text (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;
+ }
+
+ clear_entry_validation_error (self->old_password_entry);
+ 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
+password_entry_icon_press_cb (CcPasswordDialog *self)
+{
+ g_autofree gchar *pwd = NULL;
+
+ pwd = pw_generate ();
+ if (pwd == NULL)
+ return;
+
+ gtk_entry_set_text (self->password_entry, pwd);
+ gtk_entry_set_text (self->verify_entry, pwd);
+ gtk_entry_set_visibility (self->password_entry, TRUE);
+ gtk_widget_set_sensitive (GTK_WIDGET (self->verify_entry), TRUE);
+}
+
+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_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_radio_box);
+ gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, action_now_radio);
+ gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, action_login_radio);
+ gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, ok_button);
+ gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, old_password_label);
+ gtk_widget_class_bind_template_child (widget_class, CcPasswordDialog, old_password_entry);
+ 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_hint_label);
+
+ gtk_widget_class_bind_template_callback (widget_class, action_now_radio_toggled_cb);
+ 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_icon_press_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;
+
+ g_return_val_if_fail (ACT_IS_USER (user), NULL);
+
+ self = g_object_new (CC_TYPE_PASSWORD_DIALOG,
+ "use-header-bar", 1,
+ 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->action_radio_box));
+
+ visible = (act_user_get_password_mode (user) != ACT_USER_PASSWORD_MODE_NONE);
+ gtk_widget_set_visible (GTK_WIDGET (self->old_password_label), visible);
+ 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->action_radio_box));
+
+ gtk_widget_hide (GTK_WIDGET (self->old_password_label));
+ 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));
+
+ gtk_widget_grab_default (GTK_WIDGET (self->ok_button));
+
+ return self;
+}