/* -*- 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 *
 * 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 "um-password-dialog.h"
#include "um-utils.h"
#include "run-passwd.h"
#include "pw-utils.h"

struct _UmPasswordDialog {
        GtkWidget *dialog;
        GtkWidget *user_icon;
        GtkWidget *user_name;
        GtkWidget *action_label;
        GtkWidget *action_combo;
        GtkWidget *password_entry;
        GtkWidget *verify_entry;
        GtkWidget *strength_indicator;
        GtkWidget *strength_indicator_label;
        GtkWidget *normal_hint_entry;
        GtkWidget *normal_hint_label;
        GtkWidget *show_password_button;
        GtkWidget *ok_button;

        ActUser *user;
        gboolean using_ecryptfs;

        GtkWidget *old_password_label;
        GtkWidget *old_password_entry;
        gboolean   old_password_ok;

        PasswdHandler *passwd_handler;
};

typedef enum {
        UM_PASSWORD_DIALOG_MODE_NORMAL = 0,
        UM_PASSWORD_DIALOG_MODE_SET_AT_LOGIN,
        UM_PASSWORD_DIALOG_MODE_NO_PASSWORD,
        UM_PASSWORD_DIALOG_MODE_LOCK_ACCOUNT,
        UM_PASSWORD_DIALOG_MODE_UNLOCK_ACCOUNT
} UmPasswordDialogMode;

static int
update_password_strength (UmPasswordDialog *um)
{
        const gchar *password;
        const gchar *old_password;
        const gchar *username;
        gint strength_level;
        const gchar *hint;
        const gchar *long_hint;

        password = gtk_entry_get_text (GTK_ENTRY (um->password_entry));
        old_password = gtk_entry_get_text (GTK_ENTRY (um->old_password_entry));
        username = act_user_get_user_name (um->user);

        pw_strength (password, old_password, username,
                     &hint, &long_hint, &strength_level);

        gtk_level_bar_set_value (GTK_LEVEL_BAR (um->strength_indicator), strength_level);
        gtk_label_set_label (GTK_LABEL (um->strength_indicator_label), hint);
        gtk_widget_set_tooltip_text (um->strength_indicator, long_hint);
        gtk_widget_set_tooltip_text (um->strength_indicator_label, long_hint);

        return strength_level;
}

static void
generate_one_password (GtkWidget        *widget,
                       UmPasswordDialog *um)
{
        gchar *pwd;

        pwd = pw_generate ();

        gtk_entry_set_text (GTK_ENTRY (um->password_entry), pwd);
        gtk_entry_set_text (GTK_ENTRY (um->verify_entry), "");


        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (um->show_password_button),
                                      TRUE);

        g_free (pwd);
}

static void
activate_icon (GtkEntry             *entry,
               GtkEntryIconPosition  pos,
               GdkEventButton       *event,
               UmPasswordDialog     *um)
{
        generate_one_password (GTK_WIDGET (entry), um);
}

static void
populate_menu (GtkEntry         *entry,
               GtkMenu          *menu,
               UmPasswordDialog *um)
{
        GtkWidget *item;

        item = gtk_menu_item_new_with_mnemonic (_("_Generate a password"));
        g_signal_connect (item, "activate",
                          G_CALLBACK (generate_one_password), um);
        gtk_widget_show (item);
        gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
}

static void
finish_password_change (UmPasswordDialog *um)
{
        gtk_widget_hide (um->dialog);

        gtk_entry_set_text (GTK_ENTRY (um->password_entry), " ");
        gtk_entry_set_text (GTK_ENTRY (um->verify_entry), "");
        gtk_entry_set_text (GTK_ENTRY (um->normal_hint_entry), "");
        gtk_entry_set_text (GTK_ENTRY (um->old_password_entry), "");

        um_password_dialog_set_user (um, NULL);
}

static void
cancel_password_dialog (GtkButton        *button,
                        UmPasswordDialog *um)
{
        finish_password_change (um);
}

static void
dialog_closed (GtkWidget        *dialog,
               gint              response_id,
               UmPasswordDialog *um)
{
        gtk_widget_destroy (dialog);
}

static void
password_changed_cb (PasswdHandler    *handler,
                     GError           *error,
                     UmPasswordDialog *um)
{
        GtkWidget *dialog;
        const gchar *primary_text;
        const gchar *secondary_text;

        gtk_widget_set_sensitive (um->dialog, TRUE);
        gdk_window_set_cursor (gtk_widget_get_window (um->dialog), NULL);

        if (!error) {
                finish_password_change (um);
                return;
        }

        if (error->code == PASSWD_ERROR_REJECTED) {
                primary_text = error->message;
                secondary_text = _("Please choose another password.");

                gtk_entry_set_text (GTK_ENTRY (um->password_entry), "");
                gtk_widget_grab_focus (um->password_entry);

                gtk_entry_set_text (GTK_ENTRY (um->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 (GTK_ENTRY (um->old_password_entry), "");
                gtk_widget_grab_focus (um->old_password_entry);
        }
        else {
                primary_text = _("Password could not be changed");
                secondary_text = error->message;
        }

        dialog = gtk_message_dialog_new (GTK_WINDOW (um->dialog),
                                         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);
        g_signal_connect (dialog, "response",
                          G_CALLBACK (dialog_closed), um);
        gtk_window_present (GTK_WINDOW (dialog));

}

static void
accept_password_dialog (GtkButton        *button,
                        UmPasswordDialog *um)
{
        GtkTreeModel *model;
        GtkTreeIter iter;
        gint mode;
        const gchar *hint;
        const gchar *password;

        model = gtk_combo_box_get_model (GTK_COMBO_BOX (um->action_combo));
        gtk_combo_box_get_active_iter (GTK_COMBO_BOX (um->action_combo), &iter);
        gtk_tree_model_get (model, &iter, 1, &mode, -1);

        password = gtk_entry_get_text (GTK_ENTRY (um->password_entry));
        hint = gtk_entry_get_text (GTK_ENTRY (um->normal_hint_entry));

        switch (mode) {
                case UM_PASSWORD_DIALOG_MODE_NORMAL:
                        if (act_user_get_uid (um->user) == getuid ()) {
                                GdkDisplay *display;
                                GdkCursor *cursor;

                                /* 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 (um->passwd_handler, password,
                                                        (PasswdCallback) password_changed_cb, um);
                                gtk_widget_set_sensitive (um->dialog, FALSE);
                                display = gtk_widget_get_display (um->dialog);
                                cursor = gdk_cursor_new_for_display (display, GDK_WATCH);
                                gdk_window_set_cursor (gtk_widget_get_window (um->dialog), cursor);
                                gdk_display_flush (display);
                                g_object_unref (cursor);
                        } else {
                                act_user_set_password (um->user, password, hint);
                        }

                        act_user_set_password_mode (um->user, ACT_USER_PASSWORD_MODE_REGULAR);
                        break;

                case UM_PASSWORD_DIALOG_MODE_SET_AT_LOGIN:
                        act_user_set_password_mode (um->user, ACT_USER_PASSWORD_MODE_SET_AT_LOGIN);
                        act_user_set_automatic_login (um->user, FALSE);
                        break;

                case UM_PASSWORD_DIALOG_MODE_NO_PASSWORD:
                        act_user_set_password_mode (um->user, ACT_USER_PASSWORD_MODE_NONE);
                        break;

                case UM_PASSWORD_DIALOG_MODE_LOCK_ACCOUNT:
                        act_user_set_locked (um->user, TRUE);
                        act_user_set_automatic_login (um->user, FALSE);

                        /*Set the password mode to regular in case the user is currently in the nopasswdlogin group.
                        Otherwise, the user will still be able to login even though the account is disabled. */
                        act_user_set_password_mode (um->user, ACT_USER_PASSWORD_MODE_REGULAR);
                        break;

                case UM_PASSWORD_DIALOG_MODE_UNLOCK_ACCOUNT:
                        act_user_set_locked (um->user, FALSE);
                        break;

                default:
                        g_assert_not_reached ();
        }

        finish_password_change (um);
}

static void
update_sensitivity (UmPasswordDialog *um)
{
        const gchar *password, *verify;
        const gchar *old_password;
        const gchar *tooltip;
        gboolean can_change;
        int strength_level;

        password = gtk_entry_get_text (GTK_ENTRY (um->password_entry));
        verify = gtk_entry_get_text (GTK_ENTRY (um->verify_entry));
        old_password = gtk_entry_get_text (GTK_ENTRY (um->old_password_entry));

        /* Don't update the password strength if we didn't enter anything */
        if (password && *password == '\0' &&
            verify && *verify == '\0' &&
            old_password && *old_password == '\0') {
                gtk_widget_set_sensitive (um->ok_button, FALSE);
                return;
            }

        strength_level = update_password_strength (um);

        if (strength_level < 1) {
                can_change = FALSE;
                if (password[0] == '\0') {
                        tooltip = _("You need to enter a new password");
                }
                else {
                        tooltip = _("The new password is not strong enough");
                }
        }
        else if (strcmp (password, verify) != 0) {
                can_change = FALSE;
                if (verify[0] == '\0') {
                        tooltip = _("You need to confirm the password");
                }
                else {
                        tooltip = _("The passwords do not match");
                }
        }
        else if (!um->old_password_ok) {
                can_change = FALSE;
                if (old_password[0] == '\0') {
                        tooltip = _("You need to enter your current password");
                }
                else {
                        tooltip = _("The current password is not correct");
                }
        }
        else {
                can_change = TRUE;
                tooltip = NULL;
        }

        gtk_widget_set_sensitive (um->ok_button, can_change);
        gtk_widget_set_tooltip_text (um->ok_button, tooltip);
}

static void
action_changed (GtkComboBox      *combo,
                UmPasswordDialog *um)
{
        gint active;

        active = gtk_combo_box_get_active (combo);
        if (active == 0) {
                gtk_widget_set_sensitive (um->password_entry, TRUE);
                gtk_entry_set_icon_sensitive (GTK_ENTRY (um->password_entry), GTK_ENTRY_ICON_SECONDARY, TRUE);
                gtk_widget_set_sensitive (um->verify_entry, TRUE);
                gtk_widget_set_sensitive (um->old_password_entry, TRUE);
                gtk_widget_set_sensitive (um->normal_hint_entry, TRUE);
                gtk_widget_set_sensitive (um->normal_hint_label, TRUE);
                gtk_widget_set_sensitive (um->strength_indicator_label, TRUE);
                gtk_widget_set_sensitive (um->show_password_button, TRUE);

                update_sensitivity (um);
        }
        else {
                gtk_widget_set_sensitive (um->password_entry, FALSE);
                gtk_entry_set_icon_sensitive (GTK_ENTRY (um->password_entry), GTK_ENTRY_ICON_SECONDARY, FALSE);
                gtk_widget_set_sensitive (um->verify_entry, FALSE);
                gtk_widget_set_sensitive (um->old_password_entry, FALSE);
                gtk_widget_set_sensitive (um->normal_hint_entry, FALSE);
                gtk_widget_set_sensitive (um->normal_hint_label, FALSE);
                gtk_widget_set_sensitive (um->strength_indicator_label, FALSE);
                gtk_widget_set_sensitive (um->show_password_button, FALSE);
                gtk_widget_set_sensitive (um->ok_button, TRUE);
        }
}

static void
show_password_toggled (GtkToggleButton  *button,
                       UmPasswordDialog *um)
{
        gboolean active;

        active = gtk_toggle_button_get_active (button);
        gtk_entry_set_visibility (GTK_ENTRY (um->password_entry), active);
        gtk_entry_set_visibility (GTK_ENTRY (um->verify_entry), active);
}

static void
update_password_match (UmPasswordDialog *um)
{
        const char *password;
        const char *verify;

        password = gtk_entry_get_text (GTK_ENTRY (um->password_entry));
        verify = gtk_entry_get_text (GTK_ENTRY (um->verify_entry));

        if (strlen (password) > 0 && strlen (verify) > 0) {
                if (strcmp (password, verify) != 0) {
                        set_entry_validation_error (GTK_ENTRY (um->verify_entry),
                                                    _("Passwords do not match"));
                }
                else {
                        clear_entry_validation_error (GTK_ENTRY (um->verify_entry));
                }
        }
}

static void
password_entry_changed (GtkEntry         *entry,
                        GParamSpec       *pspec,
                        UmPasswordDialog *um)
{
        update_password_strength (um);
        update_sensitivity (um);
        update_password_match (um);
}

static gboolean
password_entry_focus_out (GtkWidget        *entry,
                          GdkEventFocus    *event,
                          UmPasswordDialog *um)
{
        update_password_match (um);
        return FALSE;
}

static void
verify_entry_changed (GtkEntry         *entry,
                      GParamSpec       *pspec,
                      UmPasswordDialog *um)
{
        clear_entry_validation_error (GTK_ENTRY (entry));
        update_password_strength (um);
        update_sensitivity (um);
}

static gboolean
verify_entry_focus_out (GtkWidget        *entry,
                        GdkEventFocus    *event,
                        UmPasswordDialog *um)
{
        update_password_match (um);
        return FALSE;
}

static void
entry_size_changed (GtkWidget     *entry,
                    GtkAllocation *allocation,
                    GtkWidget     *label)
{
        gtk_widget_set_size_request (label, allocation->width, -1);
}

static void
auth_cb (PasswdHandler    *handler,
         GError           *error,
         UmPasswordDialog *um)
{
        GtkTreeModel *model;
        GtkTreeIter iter;
        gint mode;

        model = gtk_combo_box_get_model (GTK_COMBO_BOX (um->action_combo));
        gtk_combo_box_get_active_iter (GTK_COMBO_BOX (um->action_combo), &iter);
        gtk_tree_model_get (model, &iter, 1, &mode, -1);

        if (error) {
                um->old_password_ok = FALSE;
                set_entry_validation_error (GTK_ENTRY (um->old_password_entry),
                                            _("Wrong password"));
        }
        else {
                um->old_password_ok = TRUE;
                clear_entry_validation_error (GTK_ENTRY (um->old_password_entry));
        }

        /* Check if we are still in normal mode */
        if (mode != UM_PASSWORD_DIALOG_MODE_NORMAL){
                return;
        }

        update_sensitivity (um);
}

static gboolean
old_password_entry_focus_out (GtkWidget        *entry,
                              GdkEventFocus    *event,
                              UmPasswordDialog *um)
{
        const char *text;

        text = gtk_entry_get_text (GTK_ENTRY (entry));
        if (strlen (text) > 0) {
                passwd_authenticate (um->passwd_handler, text,
                                     (PasswdCallback)auth_cb, um);
        }

        return FALSE;
}

static void
old_password_entry_activate (GtkWidget        *entry,
                             UmPasswordDialog *um)
{
        const char *text;

        text = gtk_entry_get_text (GTK_ENTRY (entry));
        if (strlen (text) > 0) {
                passwd_authenticate (um->passwd_handler, text,
                                     (PasswdCallback)auth_cb, um);
        }
}


static void
old_password_entry_changed (GtkEntry         *entry,
                            GParamSpec       *pspec,
                            UmPasswordDialog *um)
{
        clear_entry_validation_error (GTK_ENTRY (entry));
        um->old_password_ok = FALSE;
        update_sensitivity (um);
}

void
um_password_dialog_set_privileged (UmPasswordDialog *um,
                                   gboolean          privileged)
{
        if (privileged) {
                gtk_widget_set_visible (um->action_label, TRUE);
                gtk_widget_set_visible (um->action_combo, TRUE);
        }
        else {
                gtk_combo_box_set_active (GTK_COMBO_BOX (um->action_combo), 0);
                gtk_widget_set_visible (um->action_label, FALSE);
                gtk_widget_set_visible (um->action_combo, FALSE);
        }
}

int _is_gdm_running = -1;
gboolean
is_gdm_running (void)
{
        if (_is_gdm_running == -1) {
                GDBusProxy *proxy;
                gchar *owner_name;

                proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
                                                       G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
                                                       G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS |
                                                       G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START,
                                                       NULL,
                                                       "org.gnome.DisplayManager",
                                                       "/org/gnome/DisplayManager/Manager",
                                                       "org.gnome.DisplayManager.Manager",
                                                       NULL, NULL);

                if (proxy == NULL)
                        return FALSE;

                owner_name = g_dbus_proxy_get_name_owner (proxy);

                g_object_unref (proxy);
                g_free (owner_name);

                _is_gdm_running = (owner_name != NULL) ? 1 : 0;
        }

        return _is_gdm_running;
}

UmPasswordDialog *
um_password_dialog_new (void)
{
        GtkBuilder *builder;
        GError *error;
        UmPasswordDialog *um;
        GtkWidget *widget;
        const char *old_label;
        char *label;
        gint len;

        builder = gtk_builder_new ();

        error = NULL;
        if (!gtk_builder_add_from_resource (builder,
                                            "/org/gnome/control-center/user-accounts/password-dialog.ui",
                                            &error)) {
                g_error ("%s", error->message);
                g_error_free (error);
                return NULL;
        }

        um = g_new0 (UmPasswordDialog, 1);

        um->action_label = (GtkWidget *) gtk_builder_get_object (builder, "action-label");
        widget = (GtkWidget *) gtk_builder_get_object (builder, "action-combo");
        g_signal_connect (widget, "changed",
                          G_CALLBACK (action_changed), um);
        um->action_combo = widget;

        widget = (GtkWidget *) gtk_builder_get_object (builder, "dialog");
        g_signal_connect (widget, "delete-event",
                          G_CALLBACK (gtk_widget_hide_on_delete), NULL);
        um->dialog = widget;

        um->user_icon = (GtkWidget *) gtk_builder_get_object (builder, "user-icon");
        um->user_name = (GtkWidget *) gtk_builder_get_object (builder, "user-name");

        widget = (GtkWidget *) gtk_builder_get_object (builder, "cancel-button");
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (cancel_password_dialog), um);

        widget = (GtkWidget *) gtk_builder_get_object (builder, "ok-button");
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (accept_password_dialog), um);
        gtk_widget_grab_default (widget);
        um->ok_button = widget;

        widget = (GtkWidget *) gtk_builder_get_object (builder, "password-normal-strength-hints-label");
        old_label = gtk_label_get_label (GTK_LABEL (widget));
        label = g_strdup_printf ("<a href=\"%s\">%s</a>",
                                 "help:ubuntu-help/user-goodpassword",
                                  old_label);
        gtk_label_set_markup (GTK_LABEL (widget), label);
        g_free (label);

        widget = (GtkWidget *) gtk_builder_get_object (builder, "show-password-checkbutton");
        g_signal_connect (widget, "toggled",
                          G_CALLBACK (show_password_toggled), um);
        um->show_password_button = widget;

        widget = (GtkWidget *) gtk_builder_get_object (builder, "password-entry");
        g_signal_connect (widget, "notify::text",
                          G_CALLBACK (password_entry_changed), um);
        g_signal_connect_after (widget, "focus-out-event",
                                G_CALLBACK (password_entry_focus_out), um);
        gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);

        g_signal_connect (widget, "icon-press",
                          G_CALLBACK (activate_icon), um);
        g_signal_connect (widget, "populate-popup",
                          G_CALLBACK (populate_menu), um);

        um->password_entry = widget;

        widget = (GtkWidget *) gtk_builder_get_object (builder, "old-password-entry");
        g_signal_connect_after (widget, "focus-out-event",
                                G_CALLBACK (old_password_entry_focus_out), um);
        g_signal_connect (widget, "notify::text",
                          G_CALLBACK (old_password_entry_changed), um);
        g_signal_connect (widget, "activate",
                          G_CALLBACK (old_password_entry_activate), um);
        um->old_password_entry = widget;
        um->old_password_label = (GtkWidget *) gtk_builder_get_object (builder, "old-password-label");

        widget = (GtkWidget *) gtk_builder_get_object (builder, "verify-entry");
        g_signal_connect (widget, "notify::text",
                          G_CALLBACK (verify_entry_changed), um);
        g_signal_connect_after (widget, "focus-out-event",
                                G_CALLBACK (verify_entry_focus_out), um);
        um->verify_entry = widget;

        len = 0;
        len = MAX (len, strlen (C_("Password strength", "Too short")));
        len = MAX (len, strlen (C_("Password strength", "Weak")));
        len = MAX (len, strlen (C_("Password strength", "Fair")));
        len = MAX (len, strlen (C_("Password strength", "Good")));
        len = MAX (len, strlen (C_("Password strength", "Strong")));
        len += 2;

        widget = (GtkWidget *) gtk_builder_get_object (builder, "strength-indicator-label");
        gtk_label_set_width_chars (GTK_LABEL (widget), len);

        um->normal_hint_entry = (GtkWidget *) gtk_builder_get_object (builder, "normal-hint-entry");

        /* Label size hack.
         * This only sort-of works because the dialog is non-resizable.
         */
        widget = (GtkWidget *)gtk_builder_get_object (builder, "password-normal-hint-description-label");
        g_signal_connect (um->normal_hint_entry, "size-allocate",
                          G_CALLBACK (entry_size_changed), widget);
        um->normal_hint_label = widget;

        if (!is_gdm_running ()) {
                widget = (GtkWidget *) gtk_builder_get_object (builder, "password-normal-hint-label");
                gtk_widget_hide (widget);
                gtk_widget_hide (um->normal_hint_entry);
                gtk_widget_hide (um->normal_hint_label);
        }

        um->strength_indicator = (GtkWidget *) gtk_builder_get_object (builder, "strength-indicator");

        um->strength_indicator_label = (GtkWidget *) gtk_builder_get_object (builder, "strength-indicator-label");

        g_object_unref (builder);

        return um;
}

void
um_password_dialog_free (UmPasswordDialog *um)
{
        gtk_widget_destroy (um->dialog);

        if (um->user)
                g_object_unref (um->user);

        if (um->passwd_handler)
                passwd_destroy (um->passwd_handler);

        g_free (um);
}

static gboolean
visible_func (GtkTreeModel     *model,
              GtkTreeIter      *iter,
              UmPasswordDialog *um)
{
        if (um->user) {
                gint mode; 
                gboolean locked = act_user_get_locked (um->user);

                gtk_tree_model_get (model, iter, 1, &mode, -1);

                if (mode == 1 && !is_gdm_running ())
                        return FALSE;

                if (mode == 2 && um->using_ecryptfs)
                        return FALSE;

                /* We don't allow the current user to disable their own account,
                 * as this can lead to them being 'locked out'.
                 */
                if (mode == UM_PASSWORD_DIALOG_MODE_LOCK_ACCOUNT &&
                    (locked || act_user_get_uid (um->user) == getuid ()
                     || would_demote_only_admin (um->user)))
                        return FALSE;

                if (mode == UM_PASSWORD_DIALOG_MODE_UNLOCK_ACCOUNT && !locked)
                        return FALSE;

                return TRUE;
        }

        return TRUE;
}

void
um_password_dialog_set_user (UmPasswordDialog *um,
                             ActUser          *user)
{
        GdkPixbuf *pixbuf;
        GtkTreeModel *model;

        if (um->user) {
                g_object_unref (um->user);
                um->user = NULL;
        }
        if (user) {
                um->user = g_object_ref (user);

                um->using_ecryptfs = is_using_ecryptfs (act_user_get_user_name (user));

                pixbuf = render_user_icon (user, UM_ICON_STYLE_NONE, 48);
                gtk_image_set_from_pixbuf (GTK_IMAGE (um->user_icon), pixbuf);
                g_object_unref (pixbuf);

                gtk_label_set_label (GTK_LABEL (um->user_name),
                                     act_user_get_real_name (user));

                gtk_entry_set_text (GTK_ENTRY (um->password_entry), "");
                gtk_entry_set_text (GTK_ENTRY (um->verify_entry), "");
                gtk_entry_set_text (GTK_ENTRY (um->normal_hint_entry), "");
                gtk_entry_set_text (GTK_ENTRY (um->old_password_entry), "");
                gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (um->show_password_button), FALSE);
                if (act_user_get_uid (um->user) == getuid () &&
                    act_user_get_password_mode (um->user) == ACT_USER_PASSWORD_MODE_REGULAR) {
                        gtk_widget_show (um->old_password_label);
                        gtk_widget_show (um->old_password_entry);
                        um->old_password_ok = FALSE;
                }
                else {
                        gtk_widget_hide (um->old_password_label);
                        gtk_widget_hide (um->old_password_entry);
                        um->old_password_ok = TRUE;
                }
                if (act_user_get_uid (um->user) == getuid()) {
                        if (um->passwd_handler != NULL)
                                passwd_destroy (um->passwd_handler);
                        um->passwd_handler = passwd_init ();
                }
        }

        model = gtk_combo_box_get_model (GTK_COMBO_BOX (um->action_combo));
        if (!GTK_IS_TREE_MODEL_FILTER (model)) {
                model = gtk_tree_model_filter_new (model, NULL);
                gtk_combo_box_set_model (GTK_COMBO_BOX (um->action_combo), model);
                gtk_tree_model_filter_set_visible_func (GTK_TREE_MODEL_FILTER (model),
                                                        (GtkTreeModelFilterVisibleFunc) visible_func,
                                                        um, NULL);
        }

        gtk_tree_model_filter_refilter (GTK_TREE_MODEL_FILTER (model));
        gtk_combo_box_set_active (GTK_COMBO_BOX (um->action_combo), 0);
}

void
um_password_dialog_show (UmPasswordDialog *um,
                         GtkWindow        *parent)
{
        gtk_window_set_transient_for (GTK_WINDOW (um->dialog), parent);
        gtk_window_present (GTK_WINDOW (um->dialog));
        if (um->old_password_ok == FALSE)
                gtk_widget_grab_focus (um->old_password_entry);
        else
                gtk_widget_grab_focus (um->password_entry);
}

