/* -*- 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 3 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 "um-password-dialog.h"
#include "um-user-manager.h"
#include "um-strength-bar.h"
#include "um-utils.h"
#include "run-passwd.h"

#define MIN_PASSWORD_LEN 6

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 *generate_button;
        GtkWidget *generate_menu;
        GtkWidget *show_password_button;
        GtkWidget *ok_button;

        UmUser *user;

        GtkWidget *old_password_label;
        GtkWidget *old_password_entry;
        gboolean   old_password_ok;

        PasswdHandler *passwd_handler;
};

static void
generate_clicked (GtkButton        *button,
                  UmPasswordDialog *um)
{
        gtk_menu_popup (GTK_MENU (um->generate_menu),
                        NULL, NULL,
                        (GtkMenuPositionFunc) popup_menu_below_button, um->generate_button,
                        0, gtk_get_current_event_time ());

        gtk_widget_set_has_tooltip (um->generate_button, FALSE);
}

static void
generate_draw (GtkWidget        *widget,
               cairo_t          *cr,
               UmPasswordDialog *um)
{
        if (!gtk_widget_is_sensitive (widget))
                return;

        down_arrow (gtk_widget_get_style_context (widget),
                    cr,
                    gtk_widget_get_allocated_width (widget) - 12,
                    gtk_widget_get_allocated_height (widget) - 12,
                    12, 12);
}

static void
activate_password_item (GtkMenuItem      *item,
                        UmPasswordDialog *um)
{
        const char *password;

        password = gtk_menu_item_get_label (item);

        gtk_entry_set_text (GTK_ENTRY (um->password_entry), password);
        gtk_entry_set_text (GTK_ENTRY (um->verify_entry), "");
        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (um->show_password_button), TRUE);
        gtk_widget_grab_focus (um->verify_entry);
}

static void generate_passwords (UmPasswordDialog *um);

static void
activate_generate_item (GtkMenuItem      *item,
                        UmPasswordDialog *um)
{
        generate_passwords (um);
        generate_clicked (GTK_BUTTON (um->generate_button), um);
}

static void
on_generate_menu_unmap (GtkWidget        *menu,
                        UmPasswordDialog *um)
{
        gtk_widget_set_has_tooltip (um->generate_button, TRUE);
}

static void
generate_passwords (UmPasswordDialog *um)
{
        gint min_len, max_len;
        gchar *output, *err, *cmdline;
        gint status;
        GError *error;
        gint i;
        GtkWidget *item;

        min_len = 6;
        max_len = 12;

        if (um->generate_menu) {
                gtk_widget_destroy (um->generate_menu);
        }

        um->generate_menu = gtk_menu_new ();
        g_signal_connect (um->generate_menu, "unmap",
                          G_CALLBACK (on_generate_menu_unmap), um);

        cmdline = g_strdup_printf ("apg -n 6 -M SNC -m %d -x %d", min_len, max_len);
        error = NULL;
        output = NULL;
        err = NULL;
        if (!g_spawn_command_line_sync (cmdline, &output, &err, &status, &error)) {
                g_warning ("Failed to run apg: %s", error->message);
                g_error_free (error);
        } else if (WEXITSTATUS (status) == 0) {
                char **lines;
                lines = g_strsplit (output, "\n", 0);
                for (i = 0; lines[i]; i++) {
                        if (lines[i][0] == 0)
                                continue;

                        item = gtk_menu_item_new_with_label (lines[i]);
                        g_signal_connect (item, "activate",
                                          G_CALLBACK (activate_password_item), um);
                        gtk_widget_show (item);
                        gtk_menu_shell_append (GTK_MENU_SHELL (um->generate_menu), item);
                }
                g_strfreev (lines);
        } else {
                g_warning ("agp returned an error: %s", err);
        }

        g_free (cmdline);
        g_free (output);
        g_free (err);

        item = gtk_separator_menu_item_new ();
        gtk_widget_show (item);
        gtk_menu_shell_append (GTK_MENU_SHELL (um->generate_menu), item);

        item = gtk_menu_item_new_with_label (_("More choices..."));
        g_signal_connect (item, "activate",
                          G_CALLBACK (activate_generate_item), um);
        gtk_widget_show (item);
        gtk_menu_shell_append (GTK_MENU_SHELL (um->generate_menu), item);
}

/* This code is based on the Master Password dialog in Firefox
 * (pref-masterpass.js)
 * Original code triple-licensed under the MPL, GPL, and LGPL
 * so is license-compatible with this file
 */
static gdouble
compute_password_strength (const gchar *password)
{
        gint length;
        gint upper, lower, digit, misc;
        gint i;
        gdouble strength;

        length = strlen (password);
        upper = 0;
        lower = 0;
        digit = 0;
        misc = 0;

        for (i = 0; i < length ; i++) {
                if (g_ascii_isdigit (password[i]))
                        digit++;
                else if (g_ascii_islower (password[i]))
                        lower++;
                else if (g_ascii_isupper (password[i]))
                        upper++;
                else
                        misc++;
        }

        if (length > 5)
                length = 5;

        if (digit > 3)
                digit = 3;

        if (upper > 3)
                upper = 3;

        if (misc > 3)
                misc = 3;

        strength = ((length * 0.1) - 0.2) +
                    (digit * 0.1) +
                    (misc * 0.15) +
                    (upper * 0.1);

        strength = CLAMP (strength, 0.0, 1.0);

        return strength;
}

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));

        if (mode == 0 && um_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 {
                um_user_set_password (um->user, mode, password, hint);
                finish_password_change (um);
        }
}

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

        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));

        /* TODO: configurable policies for acceptable passwords */
        if (strlen (password) < MIN_PASSWORD_LEN) {
                can_change = FALSE;
                if (password[0] == '\0') {
                        tooltip = _("You need to enter a new password");
                }
                else {
                        tooltip = _("The new password is too short");
                }
        }
        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_widget_set_sensitive (um->generate_button, TRUE);
                gtk_widget_set_has_tooltip (um->generate_button, 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_widget_set_sensitive (um->generate_button, FALSE);
                gtk_widget_set_has_tooltip (um->generate_button, 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_strength (UmPasswordDialog *um)
{
        const gchar *password;
        gdouble strength;
        const gchar *hint;

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

        strength = compute_password_strength (password);

        if (strlen (password) < MIN_PASSWORD_LEN) {
                strength = 0.0;
                if (password[0] == '\0')
                        hint = "";
                else
                        hint = C_("Password strength", "Too short");
        }
        else if (strength < 0.50)
                hint = C_("Password strength", "Weak");
        else if (strength < 0.75)
                hint = C_("Password strength", "Fair");
        else if (strength < 0.90)
                hint = C_("Password strength", "Good");
        else
                hint = C_("Password strength", "Strong");

        um_strength_bar_set_strength (UM_STRENGTH_BAR (um->strength_indicator), strength);
        gtk_label_set_label (GTK_LABEL (um->strength_indicator_label), hint);
}

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

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)
{
        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));
                }
        }

        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)
{
        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));
        }

        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);
        }
}

UmPasswordDialog *
um_password_dialog_new (void)
{
        GtkBuilder *builder;
        GError *error;
        const gchar *filename;
        UmPasswordDialog *um;
        GtkWidget *widget;
        gint len;

        builder = gtk_builder_new ();

        error = NULL;
        filename = UIDIR "/password-dialog.ui";
        if (!g_file_test (filename, G_FILE_TEST_EXISTS))
                filename = "data/password-dialog.ui";
        if (!gtk_builder_add_from_file (builder, filename, &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, "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);
        gtk_entry_set_visibility (GTK_ENTRY (widget), FALSE);

        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);


        widget = (GtkWidget *) gtk_builder_get_object (builder, "generate-again-button");
        g_signal_connect (widget, "clicked",
                          G_CALLBACK (generate_clicked), um);
#if 0
        g_signal_connect (widget, "state-changed",
                          G_CALLBACK (generate_state_changed), um);
#endif
        um->generate_button = widget;
        g_signal_connect_after (widget, "draw",
                                G_CALLBACK (generate_draw), um);

        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;

        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);

        generate_passwords (um);

        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 = um_user_get_locked (um->user);

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

                if (mode == 3 && locked)
                        return FALSE;

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

                return TRUE;
        }

        return TRUE;
}

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

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

                pixbuf = um_user_render_icon (user, FALSE, 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),
                                     um_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 (um_user_get_uid (um->user) == getuid()) {
                        gtk_widget_show (um->old_password_label);
                        gtk_widget_show (um->old_password_entry);
                        if (um->passwd_handler != NULL)
                                passwd_destroy (um->passwd_handler);
                        um->passwd_handler = passwd_init ();
                        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;
                }
        }

        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));
        gtk_widget_grab_focus (um->password_entry);
}

