Sophie

Sophie

distrib > Mageia > 1 > i586 > media > core-updates-src > by-pkgid > d043847aa27480c55fe107cb41a6f83f > files > 4

gdm-2.32.1-1.1.mga1.src.rpm

Description: Add GDM Setup
Bug: http://bugzilla.gnome.org/show_bug.cgi?id=587750
Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gdm/+bug/395299

Index: gdm-2.30.4/configure.ac
===================================================================
--- gdm-2.30.4.orig/configure.ac	2010-08-06 12:58:12.199013001 +0200
+++ gdm-2.30.4/configure.ac	2010-08-06 12:58:16.979012995 +0200
@@ -118,6 +118,14 @@
 AC_SUBST(UPOWER_CFLAGS)
 AC_SUBST(UPOWER_LIBS)
 
+PKG_CHECK_MODULES(GDMSETUP,
+        dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
+        gtk+-2.0 >= $GTK_REQUIRED_VERSION
+	gmodule-2.0
+)
+AC_SUBST(GDMSETUP_CFLAGS)
+AC_SUBST(GDMSETUP_LIBS)
+
 PKG_CHECK_MODULES(SIMPLE_GREETER,
         dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
         gtk+-2.0 >= $GTK_REQUIRED_VERSION
@@ -1399,12 +1407,14 @@
 daemon/Makefile
 docs/Makefile
 gui/Makefile
+gui/gdmsetup/Makefile
 gui/simple-greeter/Makefile
 gui/simple-greeter/libnotificationarea/Makefile
 gui/simple-chooser/Makefile
 gui/user-switch-applet/Makefile
 utils/Makefile
 data/gdm.conf
+data/gdm.policy
 data/Makefile
 data/faces/Makefile
 data/greeter-autostart/Makefile
Index: gdm-2.30.4/gui/gdmsetup/gdm-sessions.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdm-sessions.c	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,265 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2008 Red Hat, Inc,
+ *           2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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 : William Jon McCann <mccann@jhu.edu>
+ *              Ray Strode <rstrode@redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+
+#include "gdm-sessions.h"
+
+typedef struct _GdmSessionFile {
+        char    *id;
+        char    *path;
+        char    *translated_name;
+        char    *translated_comment;
+} GdmSessionFile;
+
+static GHashTable *gdm_available_sessions_map;
+
+static gboolean gdm_sessions_map_is_initialized = FALSE;
+
+/* adapted from gnome-menus desktop-entries.c */
+static gboolean
+key_file_is_relevant (GKeyFile     *key_file)
+{
+        GError    *error;
+        gboolean   no_display;
+        gboolean   hidden;
+        gboolean   tryexec_failed;
+        char      *tryexec;
+
+        error = NULL;
+        no_display = g_key_file_get_boolean (key_file,
+                                             G_KEY_FILE_DESKTOP_GROUP,
+                                             "NoDisplay",
+                                             &error);
+        if (error) {
+                no_display = FALSE;
+                g_error_free (error);
+        }
+
+        error = NULL;
+        hidden = g_key_file_get_boolean (key_file,
+                                         G_KEY_FILE_DESKTOP_GROUP,
+                                         "Hidden",
+                                         &error);
+        if (error) {
+                hidden = FALSE;
+                g_error_free (error);
+        }
+
+        tryexec_failed = FALSE;
+        tryexec = g_key_file_get_string (key_file,
+                                         G_KEY_FILE_DESKTOP_GROUP,
+                                         "TryExec",
+                                         NULL);
+        if (tryexec) {
+                char *path;
+
+                path = g_find_program_in_path (g_strstrip (tryexec));
+
+                tryexec_failed = (path == NULL);
+
+                g_free (path);
+                g_free (tryexec);
+        }
+
+        if (no_display || hidden || tryexec_failed) {
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static void
+load_session_file (const char              *id,
+                   const char              *path)
+{
+        GKeyFile          *key_file;
+        GError            *error;
+        gboolean           res;
+        GdmSessionFile    *session;
+
+        key_file = g_key_file_new ();
+
+        error = NULL;
+        res = g_key_file_load_from_file (key_file, path, 0, &error);
+
+        if (!res) {
+                g_debug ("Failed to load \"%s\": %s\n", path, error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        if (! g_key_file_has_group (key_file, G_KEY_FILE_DESKTOP_GROUP)) {
+                goto out;
+        }
+
+        res = g_key_file_has_key (key_file, G_KEY_FILE_DESKTOP_GROUP, "Name", NULL);
+        if (! res) {
+                g_debug ("\"%s\" contains no \"Name\" key\n", path);
+                goto out;
+        }
+
+        if (!key_file_is_relevant (key_file)) {
+                g_debug ("\"%s\" is hidden or contains non-executable TryExec program\n", path);
+                goto out;
+        }
+
+        session = g_new0 (GdmSessionFile, 1);
+
+        session->id = g_strdup (id);
+        session->path = g_strdup (path);
+
+        session->translated_name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Name", NULL, NULL);
+        session->translated_comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "Comment", NULL, NULL);
+
+        g_hash_table_insert (gdm_available_sessions_map,
+                             g_strdup (id),
+                             session);
+ out:
+        g_key_file_free (key_file);
+}
+
+static void
+collect_sessions_from_directory (const char *dirname)
+{
+        GDir       *dir;
+        const char *filename;
+
+        /* FIXME: add file monitor to directory */
+
+        dir = g_dir_open (dirname, 0, NULL);
+        if (dir == NULL) {
+                return;
+        }
+
+        while ((filename = g_dir_read_name (dir))) {
+                char *id;
+                char *full_path;
+
+                if (! g_str_has_suffix (filename, ".desktop")) {
+                        continue;
+                }
+                id = g_strndup (filename, strlen (filename) - strlen (".desktop"));
+
+                full_path = g_build_filename (dirname, filename, NULL);
+
+                load_session_file (id, full_path);
+
+                g_free (id);
+                g_free (full_path);
+        }
+
+        g_dir_close (dir);
+}
+
+static void
+collect_sessions (void)
+{
+        int         i;
+        const char *search_dirs[] = {
+                "/etc/X11/sessions/",
+                DMCONFDIR "/Sessions/",
+                DATADIR "/gdm/BuiltInSessions/",
+                DATADIR "/xsessions/",
+                NULL
+        };
+
+        if (gdm_available_sessions_map == NULL) {
+                gdm_available_sessions_map = g_hash_table_new_full (g_str_hash, g_str_equal,
+                                                                    g_free, g_free);
+        }
+
+        for (i = 0; search_dirs [i] != NULL; i++) {
+                collect_sessions_from_directory (search_dirs [i]);
+        }
+}
+
+char **
+gdm_get_all_sessions (void)
+{
+        GHashTableIter iter;
+        gpointer key, value;
+        GPtrArray *array;
+
+        if (!gdm_sessions_map_is_initialized) {
+                collect_sessions ();
+
+                gdm_sessions_map_is_initialized = TRUE;
+        }
+
+        array = g_ptr_array_new ();
+        g_hash_table_iter_init (&iter, gdm_available_sessions_map);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                GdmSessionFile *session;
+
+                session = (GdmSessionFile *) value;
+
+                g_ptr_array_add (array, g_strdup (session->id));
+        }
+        g_ptr_array_add (array, NULL);
+
+        return (char **) g_ptr_array_free (array, FALSE);
+}
+
+gboolean
+gdm_get_details_for_session (const char  *id,
+                             char       **name,
+                             char       **comment)
+{
+        GdmSessionFile *session;
+
+        if (!gdm_sessions_map_is_initialized) {
+                collect_sessions ();
+
+                gdm_sessions_map_is_initialized = TRUE;
+        }
+
+        session = (GdmSessionFile *) g_hash_table_lookup (gdm_available_sessions_map,
+                                                          id);
+
+        if (session == NULL) {
+                return FALSE;
+        }
+
+        if (name != NULL) {
+                *name = g_strdup (session->translated_name);
+        }
+
+        if (comment != NULL) {
+                *comment = g_strdup (session->translated_comment);
+        }
+
+        return TRUE;
+}
Index: gdm-2.30.4/gui/gdmsetup/gdm-sessions.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdm-sessions.h	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,38 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright 2008 Red Hat, Inc.
+ * Copyright 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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: Ray Strode
+ *             William Jon McCann
+ */
+
+#ifndef __GDM_SESSIONS_H
+#define __GDM_SESSIONS_H
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+char **                gdm_get_all_sessions (void);
+gboolean               gdm_get_details_for_session (const char  *id,
+                                                    char       **name,
+                                                    char       **comment);
+
+G_END_DECLS
+
+#endif /* __GDM_SESSION_H */
Index: gdm-2.30.4/gui/gdmsetup/gdmsetup.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdmsetup.c	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,717 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <string.h>
+#include <stdlib.h>
+#include <gtk/gtk.h>
+#include <gdk/gdkkeysyms.h>
+#include <dbus/dbus-glib.h>
+#include <glib/gi18n.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-sessions.h"
+
+#define MAX_USERS_IN_COMBO_BOX 20
+
+/* User interface */
+static GtkBuilder *ui;
+static GtkWidget *dialog, *unlock_button, *option_vbox;
+static GtkWidget *user_combo, *user_entry, *delay_spin;
+static GtkWidget *session_combo;
+static GtkWidget *auto_login_radio, *login_delay_box, *login_delay_check, *sound_enable_check, *face_browser_enable_check;
+
+/* Timer to delay application of configuration */
+static guint apply_timeout = 0;
+
+/* Flag to show when information is user_login_loaded */
+static gboolean user_login_loaded = FALSE;
+static gboolean session_loaded = FALSE;
+
+/* Flag to show when information is loaded */
+static gboolean locked = TRUE;
+
+/* True if the user selection method is a combo box, False if an entry */
+static gboolean user_list_is_combo = TRUE;
+
+/* User information */
+static GdmUserManager *user_manager;
+
+/* Proxy to GDM settings */
+static DBusGProxy *proxy = NULL;
+
+
+static gboolean
+get_sound_enabled ()
+{
+    gboolean value;
+    GError *error = NULL;
+    
+    if (!dbus_g_proxy_call (proxy, "GetSoundEnabled", &error,
+                            G_TYPE_INVALID,
+                            G_TYPE_BOOLEAN, &value, G_TYPE_INVALID)) {
+        g_warning ("Error calling GetSoundEnabled(): %s", error->message);
+        return FALSE;
+    }
+  
+    return value;
+}
+
+
+static gboolean
+set_sound_enabled (gboolean enabled)
+{
+    GError *error = NULL;
+    
+    dbus_g_proxy_call (proxy, "SetSoundEnabled", &error,
+                       G_TYPE_BOOLEAN, enabled, G_TYPE_INVALID,
+                       G_TYPE_INVALID);
+    if (error) {
+        g_warning ("Error calling SetSoundEnabled(%s): %s", enabled ? "TRUE" : "FALSE", error->message);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+static gboolean
+get_face_browser_enabled ()
+{
+    gboolean value;
+    GError *error = NULL;
+    
+    if (!dbus_g_proxy_call (proxy, "GetFaceBrowserEnabled", &error,
+                            G_TYPE_INVALID,
+                            G_TYPE_BOOLEAN, &value, G_TYPE_INVALID)) {
+        g_warning ("Error calling GetFaceBrowserEnabled(): %s", error->message);
+        return FALSE;
+    }
+  
+    return value;
+}
+
+
+static gboolean
+set_face_browser_enabled (gboolean enabled)
+{
+    GError *error = NULL;
+    
+    dbus_g_proxy_call (proxy, "SetFaceBrowserEnabled", &error,
+                       G_TYPE_BOOLEAN, enabled, G_TYPE_INVALID,
+                       G_TYPE_INVALID);
+    if (error) {
+        g_warning ("Error calling SetFaceBrowserEnabled(%s): %s", enabled ? "TRUE" : "FALSE", error->message);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+static gchar *
+get_value (const gchar *key, gchar *def)
+{
+    gchar *value;
+    GError *error = NULL;
+    
+    if (!dbus_g_proxy_call (proxy, "GetValue", &error,
+                            G_TYPE_STRING, key, G_TYPE_INVALID,
+                            G_TYPE_STRING, &value, G_TYPE_INVALID)) {
+        g_warning ("Error calling GetValue('%s'): %s", key, error->message);
+        return def;
+    }
+    
+    return value;
+}
+
+
+static gboolean
+set_value (const gchar *key, const gchar *value)
+{
+    GError *error = NULL;
+    
+    dbus_g_proxy_call (proxy, "SetValue", &error,
+                       G_TYPE_STRING, key,
+                       G_TYPE_STRING, value, G_TYPE_INVALID, G_TYPE_INVALID);
+    if (error) {
+        g_warning ("Error calling SetValue('%s', '%s'): %s", key, value, error->message);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+
+static gboolean
+get_boolean_value (const gchar *key, gboolean def)
+{
+    gchar *value;
+    gboolean result;
+    
+    value = get_value (key, NULL);
+    if (!value)
+        return def;
+    result = strcmp (value, "true") == 0;
+    g_free (value);
+    return result;
+}
+
+
+static gint
+get_integer_value (const gchar *key, gint def)
+{
+    gchar *value;
+    gint result;
+    char *end;
+    
+    value = get_value (key, NULL);
+    if (!value || value[0] == '\0')
+        result = def;
+    else {
+        result = strtol (value, &end, 10);
+        if (*end != '\0')
+            result = def;
+    }
+
+    if (value)
+        g_free (value);
+    return result;
+}
+
+
+static gboolean
+set_boolean_value (const gchar *key, gboolean value)
+{
+    return set_value (key, value ? "true" : "false");
+}
+
+
+static gboolean
+set_integer_value (const gchar *key, gint value)
+{
+    char value_string[1024];
+    snprintf(value_string, 1024, "%d", value);
+    return set_value (key, value_string);
+}
+
+
+static void
+update_config ()
+{
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    gchar *user = NULL;
+    gint delay = 0;
+    gboolean auto_login = FALSE, timed_login = FALSE, error = FALSE;
+    gchar *default_session = NULL;
+    
+    if (apply_timeout != 0) {
+        g_source_remove (apply_timeout);
+        apply_timeout = 0;
+    }
+
+    if (user_list_is_combo) {
+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (user_combo));
+        if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (user_combo), &iter))
+            gtk_tree_model_get (model, &iter, 1, &user, -1);
+    }
+    else
+        user = g_strdup (gtk_entry_get_text (GTK_ENTRY (user_entry)));
+
+    model = gtk_combo_box_get_model (GTK_COMBO_BOX (session_combo));
+    if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (session_combo), &iter))
+        gtk_tree_model_get (model, &iter, 1, &default_session, -1);
+
+    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (auto_login_radio))) {
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (login_delay_check)))
+            timed_login = TRUE;
+        else
+            auto_login = TRUE;
+    }
+    
+    delay = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (delay_spin));
+
+    g_debug ("set user='%s', auto=%s, timed=%s, delay=%d, default_session=%s",
+             user,
+             auto_login ? "True" : "False",
+             timed_login ? "True" : "False", delay,
+             default_session);
+    
+    if (!set_boolean_value ("daemon/TimedLoginEnable", timed_login) ||
+        !set_boolean_value ("daemon/AutomaticLoginEnable", auto_login) ||
+        !set_value ("daemon/TimedLogin", user) ||
+        !set_value ("daemon/AutomaticLogin", user) ||    
+        !set_integer_value ("daemon/TimedLoginDelay", delay) ||
+        !set_value ("daemon/DefaultSession", default_session))
+        error = TRUE;
+
+    if (user)
+        g_free (user);
+}
+
+
+G_MODULE_EXPORT
+void
+gdm_capplet_response_cb (GtkWidget *widget, gint response_id)
+{
+    if (response_id != 1)
+        gtk_main_quit ();
+}
+
+
+static void
+unlock_response_cb (DBusGProxy     *proxy,
+                    DBusGProxyCall *call_id,
+                    void           *user_data)
+{
+    gboolean is_unlocked;
+    GError *error = NULL;
+    
+    dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_BOOLEAN, &is_unlocked, G_TYPE_INVALID);
+    if (error) {
+        g_warning ("Failed to unlock: %s", error->message);
+        g_error_free (error);
+        return;
+    }
+    
+    if (!is_unlocked)
+        return;
+    
+    locked = FALSE;
+    gtk_widget_set_sensitive (unlock_button, FALSE);
+    gtk_widget_set_sensitive (option_vbox, user_login_loaded && !locked);
+}
+
+
+G_MODULE_EXPORT
+void
+unlock_button_clicked_cb (GtkWidget *widget)
+{
+    dbus_g_proxy_begin_call (proxy, "Unlock", unlock_response_cb, NULL, NULL, G_TYPE_INVALID);
+}
+
+
+G_MODULE_EXPORT
+void
+login_delay_check_toggled_cb (GtkWidget *widget)
+{
+    gtk_widget_set_sensitive (delay_spin,
+                              gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (login_delay_check)));
+    
+    if (session_loaded && user_login_loaded)
+        update_config ();
+}
+
+
+G_MODULE_EXPORT
+void
+sound_enable_check_toggled_cb (GtkWidget *widget)
+{
+   set_sound_enabled (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+}
+
+
+G_MODULE_EXPORT
+void
+face_browser_enable_check_toggled_cb (GtkWidget *widget)
+{
+   set_face_browser_enabled (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)));
+}
+
+
+static gboolean
+delayed_apply_cb ()
+{
+    update_config ();
+    return FALSE;
+}
+
+
+G_MODULE_EXPORT
+void
+apply_config_cb (GtkWidget *widget)
+{
+    if (session_loaded && user_login_loaded) {
+        if (apply_timeout != 0)
+            g_source_remove (apply_timeout);
+        apply_timeout = g_timeout_add (200, (GSourceFunc)delayed_apply_cb, NULL);
+    }
+}
+
+
+static void
+init_login_delay ()
+{
+    gint delay;
+    
+    if (get_boolean_value ("daemon/TimedLoginEnable", FALSE))
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (login_delay_check), TRUE);
+    
+    delay = get_integer_value ("daemon/TimedLoginDelay", 30);
+
+    g_debug ("init delay=%d", delay);
+
+    gtk_spin_button_set_value (GTK_SPIN_BUTTON (delay_spin), delay);
+}
+
+
+G_MODULE_EXPORT
+void
+automatic_login_toggle_cb (GtkWidget *automatic_login_toggle)    
+{
+    gboolean automatic_login;
+
+    automatic_login = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (automatic_login_toggle));
+    gtk_widget_set_sensitive (login_delay_box, automatic_login);
+    gtk_widget_set_sensitive (user_combo, automatic_login);
+    gtk_widget_set_sensitive (user_entry, automatic_login);
+    gtk_widget_set_sensitive (face_browser_enable_check, !automatic_login);
+
+    if (session_loaded && user_login_loaded)
+        update_config ();
+}
+
+
+G_MODULE_EXPORT
+void
+default_user_combo_box_changed_cb (void)
+{
+    if (session_loaded && user_login_loaded) {
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_login_radio), TRUE);
+        update_config ();
+    }
+}
+
+G_MODULE_EXPORT
+void
+default_session_combo_box_changed_cb (void)
+{
+    if (session_loaded && user_login_loaded)
+        update_config ();
+}
+
+
+static void
+init_default_user (void)
+{
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    gboolean auto_login, timed_login, active;
+    gchar *user = NULL;
+    
+    auto_login = get_boolean_value ("daemon/AutomaticLoginEnable", FALSE);
+    timed_login = get_boolean_value ("daemon/TimedLoginEnable", FALSE);
+    
+    if (auto_login)
+        user = get_value ("daemon/AutomaticLogin", NULL);
+    if (user == NULL)
+        user = get_value ("daemon/TimedLogin", NULL);
+
+    g_debug ("init user='%s' auto=%s", user, auto_login || timed_login ? "True" : "False");
+
+    if (auto_login || timed_login)
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_login_radio), TRUE);
+    
+    if (!user_list_is_combo) {
+        if (user != NULL)
+            gtk_entry_set_text (GTK_ENTRY (user_entry), user);
+    }
+    else {
+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (user_combo));
+        active = gtk_tree_model_get_iter_first (model, &iter);
+        
+        /* If no user then use first available */
+        if (user == NULL) {
+            if (active)
+                gtk_combo_box_set_active_iter (GTK_COMBO_BOX (user_combo), &iter);
+        }
+        else {
+            while (user != NULL && active) {
+                gchar *u;
+                gboolean matched;
+            
+                gtk_tree_model_get (model, &iter, 1, &u, -1);
+                matched = strcmp (user, u) == 0;
+                g_free (u);
+                if (matched) {
+                    gtk_combo_box_set_active_iter (GTK_COMBO_BOX (user_combo), &iter);
+                    break;
+                }
+            
+                active = gtk_tree_model_iter_next (model, &iter);
+            }
+        }
+    }
+    
+    g_free (user);
+}
+
+
+static void
+init_default_session (void)
+{
+    GtkTreeModel *model;
+    GtkTreeIter iter;
+    gboolean active;
+    gchar *default_session = NULL;
+
+    default_session = get_value ("daemon/DefaultSession", NULL);
+    g_debug ("Init default session found:'%s'", default_session);
+
+    model = gtk_combo_box_get_model (GTK_COMBO_BOX (session_combo));
+    active = gtk_tree_model_get_iter_first (model, &iter);
+
+    /* If no default session then use gnome one */
+    if (default_session == NULL || strlen(default_session) == 0)
+        default_session = g_strdup("gnome");
+
+    while (active) {
+        gchar *u;
+        gboolean matched;
+
+        gtk_tree_model_get (model, &iter, 1, &u, -1);
+        matched = strcmp (default_session, u) == 0;
+        g_free (u);
+        if (matched) {
+            gtk_combo_box_set_active_iter (GTK_COMBO_BOX (session_combo), &iter);
+            break;
+        }
+
+        active = gtk_tree_model_iter_next (model, &iter);
+    }
+    session_loaded = TRUE;
+
+    g_free (default_session);
+}
+
+
+static void add_user (GdmUser *user)
+{
+    GtkListStore *model;
+    GtkTreeIter iter;
+    GString *label;
+    const gchar *home_user_dir;
+    gchar *encryption_path;
+    gchar *encryption_content;
+    GFile *encryption_file;
+    GString *encryption_mount;
+    gboolean using_encryption = FALSE;
+
+    /* don't enable autologin for user having encrypted home dir
+       there are some corner case if gdmsetup isn't run as root and another user
+       than the current one is using encrypted dir: you can't check for them if
+       they access to it. The solution is the server to refuse the change then
+       (too late in the cycle for such changes). */
+    home_user_dir = gdm_user_get_home_directory (user);
+    encryption_path = g_build_filename (home_user_dir, ".ecryptfs", "Private.mnt", NULL);
+    encryption_file = g_file_new_for_path (encryption_path);
+    if (g_file_load_contents (encryption_file, NULL, &encryption_content, NULL, NULL, NULL)) {
+        if (g_str_has_suffix (encryption_content, "\n"))
+            encryption_content[strlen(encryption_content)-1] = '\0';
+        encryption_mount = g_string_new(encryption_content);
+        if (strcmp (encryption_mount->str, home_user_dir) == 0)
+            using_encryption = TRUE;
+        g_string_free (encryption_mount, TRUE);
+    }
+    if (encryption_file)
+        g_object_unref (encryption_file);
+    g_free (encryption_path);
+
+    if (using_encryption) {
+        g_debug ("%s is using an encrypted home, not listing him for autologin", gdm_user_get_real_name (user));
+        return;
+    }
+
+    model = GTK_LIST_STORE (gtk_builder_get_object (ui, "login_user_model"));
+    gtk_list_store_append (model, &iter);
+    label = g_string_new("");
+    g_string_printf (label, "%s (%s)", gdm_user_get_real_name (user), gdm_user_get_user_name (user));
+    gtk_list_store_set (model, &iter,
+                        0, label->str,
+                        1, gdm_user_get_user_name (user),
+                        -1);
+    g_string_free (label, TRUE);
+}
+
+
+static void add_session (gchar *session_id, gchar *name, gchar *comment)
+{
+    GtkListStore *model;
+    GtkTreeIter iter;
+    GString *label;    
+
+    model = GTK_LIST_STORE (gtk_builder_get_object (ui, "session_model"));
+    gtk_list_store_append (model, &iter);
+    label = g_string_new("");
+    g_string_printf (label, "%s", name);
+    gtk_list_store_set (model, &iter,
+                        0, label->str,
+                        1, session_id,
+                        -1);
+    g_string_free (label, TRUE);
+}
+
+
+static void
+users_loaded_cb(GdmUserManager *manager)
+{
+    GSList *users, *item;
+
+    users = gdm_user_manager_list_users (user_manager);
+    
+    if (g_slist_length (users) > MAX_USERS_IN_COMBO_BOX) {
+        user_list_is_combo = FALSE;
+        gtk_widget_hide (user_combo);
+        gtk_widget_show (user_entry);
+    }
+    else {
+        for (item = users; item; item = item->next)
+            add_user ((GdmUser *) item->data);
+    }
+
+    init_default_user ();
+
+    user_login_loaded = TRUE;
+    gtk_widget_set_sensitive (option_vbox, user_login_loaded && !locked);
+}
+
+
+static void
+user_added_cb (GdmUserManager *manager, GdmUser *user)
+{
+    if (session_loaded && user_login_loaded)
+        add_user (user);
+}
+
+
+static void
+load_sessions_cb(void)
+{
+    gchar     **session_ids;
+    int        i;
+
+    session_ids = gdm_get_all_sessions ();
+
+    for (i = 0; session_ids[i] != NULL; i++) {
+        gchar *name;
+        gchar *comment;
+        if (!gdm_get_details_for_session (session_ids[i],
+                                          &name, &comment)) {
+            continue;
+        }
+        add_session (session_ids[i], name, comment);
+            g_free (name);
+            g_free (comment);
+    }
+    g_strfreev (session_ids);
+
+    init_default_session();
+}
+
+
+static void
+split_text (const gchar *text, const gchar *prefix_label_name, const gchar *suffix_label_name)
+{
+    gchar **tokens;
+    GtkWidget *prefix_label, *suffix_label;
+    
+    prefix_label = GTK_WIDGET (gtk_builder_get_object (ui, prefix_label_name));
+    suffix_label = GTK_WIDGET (gtk_builder_get_object (ui, suffix_label_name));
+    
+    tokens = g_strsplit (text, "%s", 2);
+    if (tokens[0] != NULL && tokens[1] != NULL) {
+        if (tokens[0][0] != '\0')
+            gtk_label_set_text (GTK_LABEL (prefix_label), g_strstrip (tokens[0]));
+        else
+            gtk_widget_hide (prefix_label);
+        if (tokens[1][0] != '\0')
+            gtk_label_set_text (GTK_LABEL (suffix_label), g_strstrip (tokens[1]));
+        else
+            gtk_widget_hide (suffix_label);
+    }
+    g_strfreev (tokens);
+}
+
+
+int main (int argc, char **argv)
+{
+    GtkCellRenderer *renderer;
+    DBusGConnection *connection;
+    GError *error = NULL;
+    
+    bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+    textdomain (GETTEXT_PACKAGE);
+
+    gtk_init (&argc, &argv);
+    
+    connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+    if (error) {
+        g_warning ("Failed to get system bus: %s", error->message);
+        return 1;
+    }
+    proxy = dbus_g_proxy_new_for_name (connection,
+                                       "org.gnome.DisplayManager",
+                                       "/org/gnome/DisplayManager/Settings",
+                                       "org.gnome.DisplayManager.Settings");
+
+    ui = gtk_builder_new ();
+    gtk_builder_add_from_file (ui, UIDIR "/gdmsetup.ui", &error);
+    if (error) {
+        g_warning ("Failed to load UI: %s", error->message);
+        return 1;
+    }
+    dialog = GTK_WIDGET (gtk_builder_get_object (ui, "gdm_capplet"));
+    unlock_button = GTK_WIDGET (gtk_builder_get_object (ui, "unlock_button"));
+    option_vbox = GTK_WIDGET (gtk_builder_get_object (ui, "gdm_capplet_vbox"));
+    user_combo = GTK_WIDGET (gtk_builder_get_object (ui, "default_user_combo_box"));
+    session_combo = GTK_WIDGET (gtk_builder_get_object (ui, "default_session_combo_box"));
+    user_entry = GTK_WIDGET (gtk_builder_get_object (ui, "default_user_entry"));
+    delay_spin = GTK_WIDGET (gtk_builder_get_object (ui, "login_delay_spin"));
+    auto_login_radio = GTK_WIDGET (gtk_builder_get_object (ui, "automatic_login_radio"));
+    login_delay_box = GTK_WIDGET (gtk_builder_get_object (ui, "login_delay_box"));
+    login_delay_check = GTK_WIDGET (gtk_builder_get_object (ui, "login_delay_check"));
+    sound_enable_check = GTK_WIDGET (gtk_builder_get_object (ui, "sound_enable_check"));
+    face_browser_enable_check = GTK_WIDGET (gtk_builder_get_object (ui, "face_browser_enable_check"));
+
+    if (g_file_test ("/usr/share/gdm/autostart/LoginWindow/libcanberra-ready-sound.desktop", G_FILE_TEST_EXISTS))
+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (sound_enable_check), get_sound_enabled ());
+    else
+        gtk_widget_hide (GTK_WIDGET (sound_enable_check));
+
+    gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (face_browser_enable_check), get_face_browser_enabled ());
+  
+    gtk_builder_connect_signals (ui, NULL);
+
+    /* Translators: Label for choosing which user to log in as. '%s' is replaced with an input field. */
+    split_text (_("Log in as %s automatically"), "user_prefix_label", "user_suffix_label");
+    /* Translators: Label for choosing the login delay. '%s' is replaced with an input field. */
+    split_text (_("Allow %s seconds for anyone else to log in first"), "delay_prefix_label", "delay_suffix_label");
+    /* Translators: Label for choosing the default session. '%s' is replaced with an input field. */
+    split_text (_("Select %s as default session"), "session_prefix_label", "session_suffix_label");
+    
+    init_login_delay ();
+    load_sessions_cb();
+
+    renderer = gtk_cell_renderer_text_new ();
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (user_combo), renderer, TRUE);
+    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (user_combo), renderer, "text", 0);
+
+    renderer = gtk_cell_renderer_text_new ();
+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (session_combo), renderer, TRUE);
+    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (session_combo), renderer, "text", 0);
+
+    user_manager = gdm_user_manager_ref_default ();
+    g_signal_connect (user_manager, "users-loaded", G_CALLBACK (users_loaded_cb), NULL);
+    g_signal_connect (user_manager, "user-added", G_CALLBACK (user_added_cb), NULL);
+    
+    gtk_widget_hide (user_entry);
+
+    gtk_widget_set_sensitive (option_vbox, FALSE);
+    gtk_widget_show (dialog);
+
+    gtk_main ();
+    
+    return 0;
+}
Index: gdm-2.30.4/gui/gdmsetup/gdmsetup.desktop.in
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdmsetup.desktop.in	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,12 @@
+[Desktop Entry]
+_Name=Login Screen
+_Comment=Configure login screen behavior
+Exec=gdmsetup
+Icon=gdm-setup
+StartupNotify=true
+Terminal=false
+Type=Application
+Categories=GNOME;GTK;Settings;System;
+X-GNOME-Bugzilla-Bugzilla=GNOME
+X-GNOME-Bugzilla-Product=gdm
+X-GNOME-Bugzilla-Component=general
Index: gdm-2.30.4/gui/gdmsetup/gdmsetup.ui
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdmsetup.ui	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,345 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.16"/>
+  <!-- interface-naming-policy project-wide -->
+  <object class="GtkListStore" id="login_user_model">
+    <columns>
+      <!-- column-name label -->
+      <column type="gchararray"/>
+      <!-- column-name user -->
+      <column type="gchararray"/>
+      <!-- column-name icon -->
+      <column type="GdkPixbuf"/>
+    </columns>
+  </object>
+  <object class="GtkListStore" id="session_model">
+    <columns>
+      <!-- column-name user -->
+      <column type="gchararray"/>
+      <!-- column-name session_id -->
+      <column type="gchararray"/>
+      <!-- column-name icon -->
+      <column type="GdkPixbuf"/>
+    </columns>
+  </object>
+  <object class="GtkDialog" id="gdm_capplet">
+    <property name="border_width">5</property>
+    <property name="title" translatable="yes" comments="Title of login screen settings dialog">Login Screen Settings</property>
+    <property name="resizable">False</property>
+    <property name="window_position">center</property>
+    <property name="icon_name">gdm-setup</property>
+    <property name="type_hint">normal</property>
+    <property name="has_separator">False</property>
+    <signal name="response" handler="gdm_capplet_response_cb"/>
+    <child internal-child="vbox">
+      <object class="GtkVBox" id="dialog-vbox1">
+        <property name="visible">True</property>
+        <property name="spacing">12</property>
+        <child>
+          <object class="GtkVBox" id="gdm_capplet_vbox">
+            <property name="visible">True</property>
+            <property name="spacing">18</property>
+            <child>
+              <object class="GtkLabel" id="intro_label">
+                <property name="visible">True</property>
+                <property name="xalign">0</property>
+                <property name="label" translatable="yes" comments="Description above login option radio buttons">When the computer starts up:</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkVBox" id="vbox3">
+                <property name="visible">True</property>
+                <property name="spacing">12</property>
+                <child>
+                  <object class="GtkCheckButton" id="sound_enable_check">
+                    <property name="label" translatable="yes">Play login sound</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="sound_enable_check_toggled_cb"/>
+                  </object>
+                  <packing>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkCheckButton" id="face_browser_enable_check">
+                    <property name="label" translatable="yes">Show list of users</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="draw_indicator">True</property>
+                    <signal name="toggled" handler="face_browser_enable_check_toggled_cb"/>
+                  </object>
+                  <packing>
+                    <property name="position">1</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkRadioButton" id="manual_login_radio">
+                    <property name="label" translatable="yes" comments="Radio option to set login screen to prompt for user">_Show the screen for choosing who will log in</property>
+                    <property name="visible">True</property>
+                    <property name="can_focus">True</property>
+                    <property name="receives_default">False</property>
+                    <property name="use_underline">True</property>
+                    <property name="active">True</property>
+                    <property name="draw_indicator">True</property>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">2</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkVBox" id="vbox4">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkHBox" id="hbox1">
+                        <property name="visible">True</property>
+                        <property name="spacing">6</property>
+                        <child>
+                          <object class="GtkRadioButton" id="automatic_login_radio">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">False</property>
+                            <property name="draw_indicator">True</property>
+                            <property name="group">manual_login_radio</property>
+                            <signal name="toggled" handler="automatic_login_toggle_cb"/>
+                            <child>
+                              <object class="GtkLabel" id="user_prefix_label">
+                                <property name="visible">True</property>
+                                <property name="label">_Log in as</property>
+                                <property name="use_underline">True</property>
+                              </object>
+                            </child>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">0</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkComboBox" id="default_user_combo_box">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="model">login_user_model</property>
+                            <signal name="changed" handler="default_user_combo_box_changed_cb"/>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="default_user_entry">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">&#x2022;</property>
+                            <signal name="changed" handler="apply_config_cb"/>
+                          </object>
+                          <packing>
+                            <property name="position">2</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="user_suffix_label">
+                            <property name="visible">True</property>
+                            <property name="label">automatically</property>
+                          </object>
+                          <packing>
+                            <property name="expand">False</property>
+                            <property name="position">3</property>
+                          </packing>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkAlignment" id="alignment1">
+                        <property name="visible">True</property>
+                        <property name="left_padding">16</property>
+                        <child>
+                          <object class="GtkHBox" id="login_delay_box">
+                            <property name="visible">True</property>
+                            <property name="sensitive">False</property>
+                            <property name="spacing">6</property>
+                            <child>
+                              <object class="GtkCheckButton" id="login_delay_check">
+                                <property name="visible">True</property>
+                                <property name="can_focus">True</property>
+                                <property name="receives_default">False</property>
+                                <property name="draw_indicator">True</property>
+                                <signal name="toggled" handler="login_delay_check_toggled_cb"/>
+                                <child>
+                                  <object class="GtkLabel" id="delay_prefix_label">
+                                    <property name="visible">True</property>
+                                    <property name="label">_Allow</property>
+                                    <property name="use_underline">True</property>
+                                  </object>
+                                </child>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">0</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkSpinButton" id="login_delay_spin">
+                                <property name="visible">True</property>
+                                <property name="sensitive">False</property>
+                                <property name="can_focus">True</property>
+                                <property name="invisible_char">&#x2022;</property>
+                                <property name="adjustment">adjustment1</property>
+                                <signal name="value_changed" handler="apply_config_cb"/>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">1</property>
+                              </packing>
+                            </child>
+                            <child>
+                              <object class="GtkLabel" id="delay_suffix_label">
+                                <property name="visible">True</property>
+                                <property name="label">seconds for anyone else to log in first</property>
+                              </object>
+                              <packing>
+                                <property name="expand">False</property>
+                                <property name="position">2</property>
+                              </packing>
+                            </child>
+                          </object>
+                        </child>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="position">3</property>
+                  </packing>
+                </child>
+                <child>
+                  <object class="GtkHBox" id="hbox2">
+                    <property name="visible">True</property>
+                    <property name="spacing">6</property>
+                    <child>
+                      <object class="GtkLabel" id="session_prefix_label">
+                        <property name="visible">True</property>
+                        <property name="label">Use</property>
+                        <property name="use_underline">True</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">0</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkComboBox" id="default_session_combo_box">
+                        <property name="visible">True</property>
+                        <property name="model">session_model</property>
+                        <signal name="changed" handler="default_session_combo_box_changed_cb"/>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">1</property>
+                      </packing>
+                    </child>
+                    <child>
+                      <object class="GtkLabel" id="session_suffix_label">
+                        <property name="visible">True</property>
+                        <property name="label">as default session</property>
+                      </object>
+                      <packing>
+                        <property name="expand">False</property>
+                        <property name="position">2</property>
+                      </packing>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="position">4</property>
+                  </packing>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="position">4</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child internal-child="action_area">
+          <object class="GtkHButtonBox" id="dialog-action_area1">
+            <property name="visible">True</property>
+            <property name="layout_style">end</property>
+            <child>
+              <object class="GtkButton" id="unlock_button">
+                <property name="label" translatable="yes">_Unlock</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="image">image1</property>
+                <property name="use_underline">True</property>
+                <signal name="clicked" handler="unlock_button_clicked_cb"/>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkButton" id="close_button">
+                <property name="label">gtk-close</property>
+                <property name="visible">True</property>
+                <property name="can_focus">True</property>
+                <property name="receives_default">True</property>
+                <property name="use_stock">True</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+          </object>
+          <packing>
+            <property name="expand">False</property>
+            <property name="pack_type">end</property>
+            <property name="position">0</property>
+          </packing>
+        </child>
+      </object>
+    </child>
+    <action-widgets>
+      <action-widget response="1">unlock_button</action-widget>
+      <action-widget response="-7">close_button</action-widget>
+    </action-widgets>
+  </object>
+  <object class="GtkImage" id="image1">
+    <property name="visible">True</property>
+    <property name="stock">gtk-dialog-authentication</property>
+  </object>
+  <object class="GtkSizeGroup" id="combo_size_group"/>
+  <object class="GtkSizeGroup" id="radio_size_group"/>
+  <object class="GtkAdjustment" id="adjustment1">
+    <property name="lower">1</property>
+    <property name="upper">600</property>
+    <property name="step_increment">1</property>
+  </object>
+</interface>
Index: gdm-2.30.4/gui/gdmsetup/gdm-user.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdm-user.c	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,1172 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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
+ */
+
+#include <config.h>
+
+#include <float.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-user-private.h"
+
+#define GDM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_USER, GdmUserClass))
+#define GDM_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_USER))
+#define GDM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), GDM_TYPE_USER, GdmUserClass))
+
+#define GLOBAL_FACEDIR    DATADIR "/faces"
+#define MAX_ICON_SIZE     128
+#define MAX_FILE_SIZE     65536
+#define MINIMAL_UID       100
+#define RELAX_GROUP       TRUE
+#define RELAX_OTHER       TRUE
+
+enum {
+        PROP_0,
+        PROP_MANAGER,
+        PROP_REAL_NAME,
+        PROP_USER_NAME,
+        PROP_UID,
+        PROP_HOME_DIR,
+        PROP_SHELL,
+        PROP_LOGIN_FREQUENCY,
+};
+
+enum {
+        ICON_CHANGED,
+        SESSIONS_CHANGED,
+        LAST_SIGNAL
+};
+
+struct _GdmUser {
+        GObject         parent;
+
+        GdmUserManager *manager;
+
+        uid_t           uid;
+        char           *user_name;
+        char           *real_name;
+        char           *home_dir;
+        char           *shell;
+        GList          *sessions;
+        gulong          login_frequency;
+
+        GFileMonitor   *icon_monitor;
+};
+
+typedef struct _GdmUserClass
+{
+        GObjectClass parent_class;
+
+        void (* icon_changed)     (GdmUser *user);
+        void (* sessions_changed) (GdmUser *user);
+} GdmUserClass;
+
+static void gdm_user_finalize     (GObject      *object);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GdmUser, gdm_user, G_TYPE_OBJECT)
+
+static int
+session_compare (const char *a,
+                 const char *b)
+{
+        if (a == NULL) {
+                return 1;
+        } else if (b == NULL) {
+                return -1;
+        }
+
+        return strcmp (a, b);
+}
+
+void
+_gdm_user_add_session (GdmUser    *user,
+                       const char *ssid)
+{
+        GList *li;
+
+        g_return_if_fail (GDM_IS_USER (user));
+        g_return_if_fail (ssid != NULL);
+
+        li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
+        if (li == NULL) {
+                g_debug ("GdmUser: adding session %s", ssid);
+                user->sessions = g_list_prepend (user->sessions, g_strdup (ssid));
+                g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
+        } else {
+                g_debug ("GdmUser: session already present: %s", ssid);
+        }
+}
+
+void
+_gdm_user_remove_session (GdmUser    *user,
+                          const char *ssid)
+{
+        GList *li;
+
+        g_return_if_fail (GDM_IS_USER (user));
+        g_return_if_fail (ssid != NULL);
+
+        li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
+        if (li != NULL) {
+                g_debug ("GdmUser: removing session %s", ssid);
+                g_free (li->data);
+                user->sessions = g_list_delete_link (user->sessions, li);
+                g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
+        } else {
+                g_debug ("GdmUser: session not found: %s", ssid);
+        }
+}
+
+guint
+gdm_user_get_num_sessions (GdmUser    *user)
+{
+        return g_list_length (user->sessions);
+}
+
+GList *
+gdm_user_get_sessions (GdmUser *user)
+{
+        return user->sessions;
+}
+
+static void
+_gdm_user_set_login_frequency (GdmUser *user,
+                               gulong   login_frequency)
+{
+        user->login_frequency = login_frequency;
+        g_object_notify (G_OBJECT (user), "login-frequency");
+}
+
+static void
+gdm_user_set_property (GObject      *object,
+                       guint         param_id,
+                       const GValue *value,
+                       GParamSpec   *pspec)
+{
+        GdmUser *user;
+
+        user = GDM_USER (object);
+
+        switch (param_id) {
+        case PROP_MANAGER:
+                user->manager = g_value_get_object (value);
+                g_assert (user->manager);
+                break;
+        case PROP_LOGIN_FREQUENCY:
+                _gdm_user_set_login_frequency (user, g_value_get_ulong (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                break;
+        }
+}
+
+static void
+gdm_user_get_property (GObject    *object,
+                       guint       param_id,
+                       GValue     *value,
+                       GParamSpec *pspec)
+{
+        GdmUser *user;
+
+        user = GDM_USER (object);
+
+        switch (param_id) {
+        case PROP_MANAGER:
+                g_value_set_object (value, user->manager);
+                break;
+        case PROP_USER_NAME:
+                g_value_set_string (value, user->user_name);
+                break;
+        case PROP_REAL_NAME:
+                g_value_set_string (value, user->real_name);
+                break;
+        case PROP_HOME_DIR:
+                g_value_set_string (value, user->home_dir);
+                break;
+        case PROP_UID:
+                g_value_set_ulong (value, user->uid);
+                break;
+        case PROP_SHELL:
+                g_value_set_string (value, user->shell);
+                break;
+        case PROP_LOGIN_FREQUENCY:
+                g_value_set_ulong (value, user->login_frequency);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                break;
+        }
+}
+
+static void
+gdm_user_class_init (GdmUserClass *class)
+{
+        GObjectClass *gobject_class;
+
+        gobject_class = G_OBJECT_CLASS (class);
+
+        gobject_class->set_property = gdm_user_set_property;
+        gobject_class->get_property = gdm_user_get_property;
+        gobject_class->finalize = gdm_user_finalize;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_MANAGER,
+                                         g_param_spec_object ("manager",
+                                                              _("Manager"),
+                                                              _("The user manager object this user is controlled by."),
+                                                              GDM_TYPE_USER_MANAGER,
+                                                              (G_PARAM_READWRITE |
+                                                               G_PARAM_CONSTRUCT_ONLY)));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_REAL_NAME,
+                                         g_param_spec_string ("real-name",
+                                                              "Real Name",
+                                                              "The real name to display for this user.",
+                                                              NULL,
+                                                              G_PARAM_READABLE));
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_UID,
+                                         g_param_spec_ulong ("uid",
+                                                             "User ID",
+                                                             "The UID for this user.",
+                                                             0, G_MAXULONG, 0,
+                                                             G_PARAM_READABLE));
+        g_object_class_install_property (gobject_class,
+                                         PROP_USER_NAME,
+                                         g_param_spec_string ("user-name",
+                                                              "User Name",
+                                                              "The login name for this user.",
+                                                              NULL,
+                                                              G_PARAM_READABLE));
+        g_object_class_install_property (gobject_class,
+                                         PROP_HOME_DIR,
+                                         g_param_spec_string ("home-directory",
+                                                              "Home Directory",
+                                                              "The home directory for this user.",
+                                                              NULL,
+                                                              G_PARAM_READABLE));
+        g_object_class_install_property (gobject_class,
+                                         PROP_SHELL,
+                                         g_param_spec_string ("shell",
+                                                              "Shell",
+                                                              "The shell for this user.",
+                                                              NULL,
+                                                              G_PARAM_READABLE));
+        g_object_class_install_property (gobject_class,
+                                         PROP_LOGIN_FREQUENCY,
+                                         g_param_spec_ulong ("login-frequency",
+                                                             "login frequency",
+                                                             "login frequency",
+                                                             0,
+                                                             G_MAXULONG,
+                                                             0,
+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+
+        signals [ICON_CHANGED] =
+                g_signal_new ("icon-changed",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserClass, icon_changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [SESSIONS_CHANGED] =
+                g_signal_new ("sessions-changed",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserClass, sessions_changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+}
+
+
+static void
+on_icon_monitor_changed (GFileMonitor     *monitor,
+                         GFile            *file,
+                         GFile            *other_file,
+                         GFileMonitorEvent event_type,
+                         GdmUser          *user)
+{
+        g_debug ("Icon changed: %d", event_type);
+
+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
+                return;
+        }
+
+        _gdm_user_icon_changed (user);
+}
+
+static void
+update_icon_monitor (GdmUser *user)
+{
+        GFile  *file;
+        GError *error;
+        char   *path;
+
+        if (user->home_dir == NULL) {
+                return;
+        }
+
+        if (user->icon_monitor != NULL) {
+                g_file_monitor_cancel (user->icon_monitor);
+                user->icon_monitor = NULL;
+        }
+
+        path = g_build_filename (user->home_dir, ".face", NULL);
+        g_debug ("adding monitor for '%s'", path);
+        file = g_file_new_for_path (path);
+        error = NULL;
+        user->icon_monitor = g_file_monitor_file (file,
+                                                  G_FILE_MONITOR_NONE,
+                                                  NULL,
+                                                  &error);
+        if (user->icon_monitor != NULL) {
+                g_signal_connect (user->icon_monitor,
+                                  "changed",
+                                  G_CALLBACK (on_icon_monitor_changed),
+                                  user);
+        } else {
+                g_warning ("Unable to monitor %s: %s", path, error->message);
+                g_error_free (error);
+        }
+        g_object_unref (file);
+        g_free (path);
+}
+
+static void
+gdm_user_init (GdmUser *user)
+{
+        user->manager = NULL;
+        user->user_name = NULL;
+        user->real_name = NULL;
+        user->sessions = NULL;
+}
+
+static void
+gdm_user_finalize (GObject *object)
+{
+        GdmUser *user;
+
+        user = GDM_USER (object);
+
+        g_file_monitor_cancel (user->icon_monitor);
+
+        g_free (user->user_name);
+        g_free (user->real_name);
+
+        if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
+                (*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
+}
+
+/**
+ * _gdm_user_update:
+ * @user: the user object to update.
+ * @pwent: the user data to use.
+ *
+ * Updates the properties of @user using the data in @pwent.
+ *
+ * Since: 1.0
+ **/
+void
+_gdm_user_update (GdmUser             *user,
+                  const struct passwd *pwent)
+{
+        gchar *real_name;
+
+        g_return_if_fail (GDM_IS_USER (user));
+        g_return_if_fail (pwent != NULL);
+
+        g_object_freeze_notify (G_OBJECT (user));
+
+        /* Display Name */
+        if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
+                gchar *first_comma;
+
+                first_comma = strchr (pwent->pw_gecos, ',');
+                if (first_comma) {
+                        real_name = g_strndup (pwent->pw_gecos,
+                                                  (first_comma - pwent->pw_gecos));
+                } else {
+                        real_name = g_strdup (pwent->pw_gecos);
+                }
+
+                if (real_name[0] == '\0') {
+                        g_free (real_name);
+                        real_name = NULL;
+                }
+        } else {
+                real_name = NULL;
+        }
+
+        if ((real_name && !user->real_name) ||
+            (!real_name && user->real_name) ||
+            (real_name &&
+             user->real_name &&
+             strcmp (real_name, user->real_name) != 0)) {
+                g_free (user->real_name);
+                user->real_name = real_name;
+                g_object_notify (G_OBJECT (user), "real-name");
+        } else {
+                g_free (real_name);
+        }
+
+        /* UID */
+        if (pwent->pw_uid != user->uid) {
+                user->uid = pwent->pw_uid;
+                g_object_notify (G_OBJECT (user), "uid");
+        }
+
+        /* Username */
+        if ((pwent->pw_name && !user->user_name) ||
+            (!pwent->pw_name && user->user_name) ||
+            (pwent->pw_name &&
+             user->user_name &&
+             strcmp (user->user_name, pwent->pw_name) != 0)) {
+                g_free (user->user_name);
+                user->user_name = g_strdup (pwent->pw_name);
+                g_object_notify (G_OBJECT (user), "user-name");
+        }
+
+        /* Home Directory */
+        if ((pwent->pw_dir && !user->home_dir) ||
+            (!pwent->pw_dir && user->home_dir) ||
+            strcmp (user->home_dir, pwent->pw_dir) != 0) {
+                g_free (user->home_dir);
+                user->home_dir = g_strdup (pwent->pw_dir);
+                g_object_notify (G_OBJECT (user), "home-directory");
+                g_signal_emit (user, signals[ICON_CHANGED], 0);
+        }
+
+        /* Shell */
+        if ((pwent->pw_shell && !user->shell) ||
+            (!pwent->pw_shell && user->shell) ||
+            (pwent->pw_shell &&
+             user->shell &&
+             strcmp (user->shell, pwent->pw_shell) != 0)) {
+                g_free (user->shell);
+                user->shell = g_strdup (pwent->pw_shell);
+                g_object_notify (G_OBJECT (user), "shell");
+        }
+
+        update_icon_monitor (user);
+
+        g_object_thaw_notify (G_OBJECT (user));
+}
+
+/**
+ * _gdm_user_icon_changed:
+ * @user: the user to emit the signal for.
+ *
+ * Emits the "icon-changed" signal for @user.
+ *
+ * Since: 1.0
+ **/
+void
+_gdm_user_icon_changed (GdmUser *user)
+{
+        g_return_if_fail (GDM_IS_USER (user));
+
+        g_signal_emit (user, signals[ICON_CHANGED], 0);
+}
+
+/**
+ * gdm_user_get_uid:
+ * @user: the user object to examine.
+ *
+ * Retrieves the ID of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ *  freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+uid_t
+gdm_user_get_uid (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), -1);
+
+        return user->uid;
+}
+
+/**
+ * gdm_user_get_real_name:
+ * @user: the user object to examine.
+ *
+ * Retrieves the display name of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ *  freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+G_CONST_RETURN gchar *
+gdm_user_get_real_name (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+        return (user->real_name ? user->real_name : user->user_name);
+}
+
+/**
+ * gdm_user_get_user_name:
+ * @user: the user object to examine.
+ *
+ * Retrieves the login name of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ *  freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_user_name (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+        return user->user_name;
+}
+
+/**
+ * gdm_user_get_home_directory:
+ * @user: the user object to examine.
+ *
+ * Retrieves the home directory of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ *  freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_home_directory (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+        return user->home_dir;
+}
+
+/**
+ * gdm_user_get_shell:
+ * @user: the user object to examine.
+ *
+ * Retrieves the login shell of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ *  freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_shell (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+        return user->shell;
+}
+
+gulong
+gdm_user_get_login_frequency (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), 0);
+
+        return user->login_frequency;
+}
+
+int
+gdm_user_collate (GdmUser *user1,
+                  GdmUser *user2)
+{
+        const char *str1;
+        const char *str2;
+        gulong      num1;
+        gulong      num2;
+
+        g_return_val_if_fail (user1 == NULL || GDM_IS_USER (user1), 0);
+        g_return_val_if_fail (user2 == NULL || GDM_IS_USER (user2), 0);
+
+        if (user1->real_name != NULL) {
+                str1 = user1->real_name;
+        } else {
+                str1 = user1->user_name;
+        }
+
+        if (user2->real_name != NULL) {
+                str2 = user2->real_name;
+        } else {
+                str2 = user2->user_name;
+        }
+
+        num1 = user1->login_frequency;
+        num2 = user2->login_frequency;
+        g_debug ("Login freq 1=%u 2=%u", (guint)num1, (guint)num2);
+        if (num1 > num2) {
+                return -1;
+        }
+
+        if (num1 < num2) {
+                return 1;
+        }
+
+        /* if login frequency is equal try names */
+        if (str1 == NULL && str2 != NULL) {
+                return -1;
+        }
+
+        if (str1 != NULL && str2 == NULL) {
+                return 1;
+        }
+
+        if (str1 == NULL && str2 == NULL) {
+                return 0;
+        }
+
+        return g_utf8_collate (str1, str2);
+}
+
+static gboolean
+check_user_file (const char *filename,
+                 uid_t       user,
+                 gssize      max_file_size,
+                 gboolean    relax_group,
+                 gboolean    relax_other)
+{
+        struct stat fileinfo;
+
+        if (max_file_size < 0) {
+                max_file_size = G_MAXSIZE;
+        }
+
+        /* Exists/Readable? */
+        if (stat (filename, &fileinfo) < 0) {
+                return FALSE;
+        }
+
+        /* Is a regular file */
+        if (G_UNLIKELY (!S_ISREG (fileinfo.st_mode))) {
+                return FALSE;
+        }
+
+        /* Owned by user? */
+        if (G_UNLIKELY (fileinfo.st_uid != user)) {
+                return FALSE;
+        }
+
+        /* Group not writable or relax_group? */
+        if (G_UNLIKELY ((fileinfo.st_mode & S_IWGRP) == S_IWGRP && !relax_group)) {
+                return FALSE;
+        }
+
+        /* Other not writable or relax_other? */
+        if (G_UNLIKELY ((fileinfo.st_mode & S_IWOTH) == S_IWOTH && !relax_other)) {
+                return FALSE;
+        }
+
+        /* Size is kosher? */
+        if (G_UNLIKELY (fileinfo.st_size > max_file_size)) {
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static char *
+get_filesystem_type (const char *path)
+{
+        GFile      *file;
+        GFileInfo  *file_info;
+        GError     *error;
+        char       *filesystem_type;
+
+        file = g_file_new_for_path (path);
+        error = NULL;
+        file_info = g_file_query_filesystem_info (file,
+                                                  G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
+                                                  NULL,
+                                                  &error);
+        if (file_info == NULL) {
+                g_warning ("Unable to query filesystem type for %s: %s", path, error->message);
+                g_error_free (error);
+                g_object_unref (file);
+                return NULL;
+        }
+
+        filesystem_type = g_strdup (g_file_info_get_attribute_string (file_info,
+                                                                      G_FILE_ATTRIBUTE_FILESYSTEM_TYPE));
+        if (filesystem_type == NULL) {
+                g_warning ("GIO returned NULL filesystem type for %s", path);
+        }
+
+        g_object_unref (file);
+        g_object_unref (file_info);
+
+        return filesystem_type;
+}
+
+static GdkPixbuf *
+render_icon_from_home (GdmUser *user,
+                       int      icon_size)
+{
+        GdkPixbuf  *retval;
+        char       *path;
+        gboolean    is_local;
+        gboolean    is_autofs;
+        gboolean    res;
+        char       *filesystem_type;
+
+        is_local = FALSE;
+
+        /* special case: look at parent of home to detect autofs
+           this is so we don't try to trigger an automount */
+        path = g_path_get_dirname (user->home_dir);
+        filesystem_type = get_filesystem_type (path);
+        is_autofs = (filesystem_type != NULL && strcmp (filesystem_type, "autofs") == 0);
+        g_free (filesystem_type);
+        g_free (path);
+
+        if (is_autofs) {
+                return NULL;
+        }
+
+        /* now check that home dir itself is local */
+        filesystem_type = get_filesystem_type (user->home_dir);
+        is_local = ((filesystem_type != NULL) &&
+                    (strcmp (filesystem_type, "nfs") != 0) &&
+                    (strcmp (filesystem_type, "afs") != 0) &&
+                    (strcmp (filesystem_type, "autofs") != 0) &&
+                    (strcmp (filesystem_type, "unknown") != 0) &&
+                    (strcmp (filesystem_type, "ncpfs") != 0));
+        g_free (filesystem_type);
+
+        /* only look at local home directories so we don't try to
+           read from remote (e.g. NFS) volumes */
+        if (! is_local) {
+                return NULL;
+        }
+
+        /* First, try "~/.face" */
+        path = g_build_filename (user->home_dir, ".face", NULL);
+        res = check_user_file (path,
+                               user->uid,
+                               MAX_FILE_SIZE,
+                               RELAX_GROUP,
+                               RELAX_OTHER);
+        if (res) {
+                retval = gdk_pixbuf_new_from_file_at_size (path,
+                                                           icon_size,
+                                                           icon_size,
+                                                           NULL);
+        } else {
+                retval = NULL;
+        }
+        g_free (path);
+
+        /* Next, try "~/.face.icon" */
+        if (retval == NULL) {
+                path = g_build_filename (user->home_dir,
+                                         ".face.icon",
+                                         NULL);
+                res = check_user_file (path,
+                                       user->uid,
+                                       MAX_FILE_SIZE,
+                                       RELAX_GROUP,
+                                       RELAX_OTHER);
+                if (res) {
+                        retval = gdk_pixbuf_new_from_file_at_size (path,
+                                                                   icon_size,
+                                                                   icon_size,
+                                                                   NULL);
+                } else {
+                        retval = NULL;
+                }
+
+                g_free (path);
+        }
+
+        /* Still nothing, try the user's personal GDM config */
+        if (retval == NULL) {
+                path = g_build_filename (user->home_dir,
+                                         ".gnome",
+                                         "gdm",
+                                         NULL);
+                res = check_user_file (path,
+                                       user->uid,
+                                       MAX_FILE_SIZE,
+                                       RELAX_GROUP,
+                                       RELAX_OTHER);
+                if (res) {
+                        GKeyFile *keyfile;
+                        char     *icon_path;
+
+                        keyfile = g_key_file_new ();
+                        g_key_file_load_from_file (keyfile,
+                                                   path,
+                                                   G_KEY_FILE_NONE,
+                                                   NULL);
+
+                        icon_path = g_key_file_get_string (keyfile,
+                                                           "face",
+                                                           "picture",
+                                                           NULL);
+                        res = check_user_file (icon_path,
+                                               user->uid,
+                                               MAX_FILE_SIZE,
+                                               RELAX_GROUP,
+                                               RELAX_OTHER);
+                        if (icon_path && res) {
+                                retval = gdk_pixbuf_new_from_file_at_size (path,
+                                                                           icon_size,
+                                                                           icon_size,
+                                                                           NULL);
+                        } else {
+                                retval = NULL;
+                        }
+
+                        g_free (icon_path);
+                        g_key_file_free (keyfile);
+                } else {
+                        retval = NULL;
+                }
+
+                g_free (path);
+        }
+
+        return retval;
+}
+
+static void
+curved_rectangle (cairo_t *cr,
+                  double   x0,
+                  double   y0,
+                  double   width,
+                  double   height,
+                  double   radius)
+{
+        double x1;
+        double y1;
+
+        x1 = x0 + width;
+        y1 = y0 + height;
+
+        if (width < FLT_EPSILON || height < FLT_EPSILON) {
+                return;
+        }
+
+        if (width / 2 < radius) {
+                if (height / 2 < radius) {
+                        cairo_move_to  (cr, x0, (y0 + y1) / 2);
+                        cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0);
+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
+                        cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
+                } else {
+                        cairo_move_to  (cr, x0, y0 + radius);
+                        cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0);
+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
+                        cairo_line_to (cr, x1, y1 - radius);
+                        cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
+                }
+        } else {
+                if (height / 2 < radius) {
+                        cairo_move_to  (cr, x0, (y0 + y1) / 2);
+                        cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0);
+                        cairo_line_to (cr, x1 - radius, y0);
+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
+                        cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
+                        cairo_line_to (cr, x0 + radius, y1);
+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
+                } else {
+                        cairo_move_to  (cr, x0, y0 + radius);
+                        cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
+                        cairo_line_to (cr, x1 - radius, y0);
+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
+                        cairo_line_to (cr, x1, y1 - radius);
+                        cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
+                        cairo_line_to (cr, x0 + radius, y1);
+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
+                }
+        }
+
+        cairo_close_path (cr);
+}
+
+static cairo_surface_t *
+surface_from_pixbuf (GdkPixbuf *pixbuf)
+{
+        cairo_surface_t *surface;
+        cairo_t         *cr;
+
+        surface = cairo_image_surface_create (gdk_pixbuf_get_has_alpha (pixbuf) ?
+                                              CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
+                                              gdk_pixbuf_get_width (pixbuf),
+                                              gdk_pixbuf_get_height (pixbuf));
+        cr = cairo_create (surface);
+        gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
+        cairo_paint (cr);
+        cairo_destroy (cr);
+
+        return surface;
+}
+
+/**
+ * go_cairo_convert_data_to_pixbuf:
+ * @src: a pointer to pixel data in cairo format
+ * @dst: a pointer to pixel data in pixbuf format
+ * @width: image width
+ * @height: image height
+ * @rowstride: data rowstride
+ *
+ * Converts the pixel data stored in @src in CAIRO_FORMAT_ARGB32 cairo format
+ * to GDK_COLORSPACE_RGB pixbuf format and move them
+ * to @dst. If @src == @dst, pixel are converted in place.
+ **/
+
+static void
+go_cairo_convert_data_to_pixbuf (unsigned char *dst,
+                                 unsigned char const *src,
+                                 int width,
+                                 int height,
+                                 int rowstride)
+{
+        int i,j;
+        unsigned int t;
+        unsigned char a, b, c;
+
+        g_return_if_fail (dst != NULL);
+
+#define MULT(d,c,a,t) G_STMT_START { t = (a)? c * 255 / a: 0; d = t;} G_STMT_END
+
+        if (src == dst || src == NULL) {
+                for (i = 0; i < height; i++) {
+                        for (j = 0; j < width; j++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                                MULT(a, dst[2], dst[3], t);
+                                MULT(b, dst[1], dst[3], t);
+                                MULT(c, dst[0], dst[3], t);
+                                dst[0] = a;
+                                dst[1] = b;
+                                dst[2] = c;
+#else
+                                MULT(a, dst[1], dst[0], t);
+                                MULT(b, dst[2], dst[0], t);
+                                MULT(c, dst[3], dst[0], t);
+                                dst[3] = dst[0];
+                                dst[0] = a;
+                                dst[1] = b;
+                                dst[2] = c;
+#endif
+                                dst += 4;
+                        }
+                        dst += rowstride - width * 4;
+                }
+        } else {
+                for (i = 0; i < height; i++) {
+                        for (j = 0; j < width; j++) {
+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
+                                MULT(dst[0], src[2], src[3], t);
+                                MULT(dst[1], src[1], src[3], t);
+                                MULT(dst[2], src[0], src[3], t);
+                                dst[3] = src[3];
+#else
+                                MULT(dst[0], src[1], src[0], t);
+                                MULT(dst[1], src[2], src[0], t);
+                                MULT(dst[2], src[3], src[0], t);
+                                dst[3] = src[0];
+#endif
+                                src += 4;
+                                dst += 4;
+                        }
+                        src += rowstride - width * 4;
+                        dst += rowstride - width * 4;
+                }
+        }
+#undef MULT
+}
+
+static void
+cairo_to_pixbuf (guint8    *src_data,
+                 GdkPixbuf *dst_pixbuf)
+{
+        unsigned char *src;
+        unsigned char *dst;
+        guint          w;
+        guint          h;
+        guint          rowstride;
+
+        w = gdk_pixbuf_get_width (dst_pixbuf);
+        h = gdk_pixbuf_get_height (dst_pixbuf);
+        rowstride = gdk_pixbuf_get_rowstride (dst_pixbuf);
+
+        dst = gdk_pixbuf_get_pixels (dst_pixbuf);
+        src = src_data;
+
+        go_cairo_convert_data_to_pixbuf (dst, src, w, h, rowstride);
+}
+
+static GdkPixbuf *
+frame_pixbuf (GdkPixbuf *source)
+{
+        GdkPixbuf       *dest;
+        cairo_t         *cr;
+        cairo_surface_t *surface;
+        guint            w;
+        guint            h;
+        guint            rowstride;
+        int              frame_width;
+        double           radius;
+        guint8          *data;
+
+        frame_width = 2;
+
+        w = gdk_pixbuf_get_width (source) + frame_width * 2;
+        h = gdk_pixbuf_get_height (source) + frame_width * 2;
+        radius = w / 3.0;
+
+        dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
+                               TRUE,
+                               8,
+                               w,
+                               h);
+        rowstride = gdk_pixbuf_get_rowstride (dest);
+
+
+        data = g_new0 (guint8, h * rowstride);
+
+        surface = cairo_image_surface_create_for_data (data,
+                                                       CAIRO_FORMAT_ARGB32,
+                                                       w,
+                                                       h,
+                                                       rowstride);
+        cr = cairo_create (surface);
+        cairo_surface_destroy (surface);
+
+        /* set up image */
+        cairo_rectangle (cr, 0, 0, w, h);
+        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
+        cairo_fill (cr);
+
+        curved_rectangle (cr, frame_width, frame_width,
+                          w - frame_width * 2, h - frame_width * 2,
+                          radius);
+        cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
+        cairo_fill_preserve (cr);
+
+        surface = surface_from_pixbuf (source);
+        cairo_set_source_surface (cr, surface, frame_width, frame_width);
+        cairo_fill (cr);
+        cairo_surface_destroy (surface);
+
+        cairo_to_pixbuf (data, dest);
+
+        cairo_destroy (cr);
+        g_free (data);
+
+        return dest;
+}
+
+GdkPixbuf *
+gdm_user_render_icon (GdmUser   *user,
+                      gint       icon_size)
+{
+        GdkPixbuf    *pixbuf;
+        GdkPixbuf    *framed;
+        char         *path;
+        char         *tmp;
+        gboolean      res;
+
+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
+        g_return_val_if_fail (icon_size > 12, NULL);
+
+        path = NULL;
+
+        pixbuf = render_icon_from_home (user, icon_size);
+        if (pixbuf != NULL) {
+                goto out;
+        }
+
+        /* Try ${GlobalFaceDir}/${username} */
+        path = g_build_filename (GLOBAL_FACEDIR, user->user_name, NULL);
+        res = check_user_file (path,
+                               user->uid,
+                               MAX_FILE_SIZE,
+                               RELAX_GROUP,
+                               RELAX_OTHER);
+        if (res) {
+                pixbuf = gdk_pixbuf_new_from_file_at_size (path,
+                                                           icon_size,
+                                                           icon_size,
+                                                           NULL);
+        } else {
+                pixbuf = NULL;
+        }
+
+        g_free (path);
+        if (pixbuf != NULL) {
+                goto out;
+        }
+
+        /* Finally, ${GlobalFaceDir}/${username}.png */
+        tmp = g_strconcat (user->user_name, ".png", NULL);
+        path = g_build_filename (GLOBAL_FACEDIR, tmp, NULL);
+        g_free (tmp);
+        res = check_user_file (path,
+                               user->uid,
+                               MAX_FILE_SIZE,
+                               RELAX_GROUP,
+                               RELAX_OTHER);
+        if (res) {
+                pixbuf = gdk_pixbuf_new_from_file_at_size (path,
+                                                           icon_size,
+                                                           icon_size,
+                                                           NULL);
+        } else {
+                pixbuf = NULL;
+        }
+        g_free (path);
+ out:
+
+        if (pixbuf != NULL) {
+                framed = frame_pixbuf (pixbuf);
+                if (framed != NULL) {
+                        g_object_unref (pixbuf);
+                        pixbuf = framed;
+                }
+        }
+
+        return pixbuf;
+}
Index: gdm-2.30.4/gui/gdmsetup/gdm-user.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdm-user.h	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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
+ */
+
+/*
+ * Facade object for user data, owned by GdmUserManager
+ */
+
+#ifndef __GDM_USER__
+#define __GDM_USER__ 1
+
+#include <sys/types.h>
+#include <gtk/gtk.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER (gdm_user_get_type ())
+#define GDM_USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_USER, GdmUser))
+#define GDM_IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_USER))
+
+typedef struct _GdmUser GdmUser;
+
+GType                 gdm_user_get_type            (void) G_GNUC_CONST;
+
+uid_t                 gdm_user_get_uid             (GdmUser   *user);
+G_CONST_RETURN char  *gdm_user_get_user_name       (GdmUser   *user);
+G_CONST_RETURN char  *gdm_user_get_real_name       (GdmUser   *user);
+G_CONST_RETURN char  *gdm_user_get_home_directory  (GdmUser   *user);
+G_CONST_RETURN char  *gdm_user_get_shell           (GdmUser   *user);
+guint                 gdm_user_get_num_sessions    (GdmUser   *user);
+GList                *gdm_user_get_sessions        (GdmUser   *user);
+gulong                gdm_user_get_login_frequency (GdmUser   *user);
+
+GdkPixbuf            *gdm_user_render_icon         (GdmUser   *user,
+                                                    gint       icon_size);
+
+gint                  gdm_user_collate             (GdmUser   *user1,
+                                                    GdmUser   *user2);
+
+G_END_DECLS
+
+#endif
Index: gdm-2.30.4/gui/gdmsetup/gdm-user-manager.c
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdm-user-manager.c	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,1658 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <errno.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#ifdef HAVE_PATHS_H
+#include <paths.h>
+#endif /* HAVE_PATHS_H */
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <dbus/dbus.h>
+#include <dbus/dbus-glib.h>
+#include <dbus/dbus-glib-lowlevel.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-user-private.h"
+
+#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
+
+#define CK_NAME      "org.freedesktop.ConsoleKit"
+#define CK_PATH      "/org/freedesktop/ConsoleKit"
+#define CK_INTERFACE "org.freedesktop.ConsoleKit"
+
+#define CK_MANAGER_PATH      "/org/freedesktop/ConsoleKit/Manager"
+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
+#define CK_SEAT_INTERFACE    "org.freedesktop.ConsoleKit.Seat"
+#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
+
+/* Prefs Defaults */
+#define DEFAULT_ALLOW_ROOT      TRUE
+#define DEFAULT_MAX_ICON_SIZE   128
+#define DEFAULT_USER_MAX_FILE   65536
+
+#ifdef __sun
+#define DEFAULT_MINIMAL_UID     100
+#else
+#define DEFAULT_MINIMAL_UID     500
+#endif
+
+#ifndef _PATH_SHELLS
+#define _PATH_SHELLS    "/etc/shells"
+#endif
+#define PATH_PASSWD     "/etc/passwd"
+
+#define DEFAULT_GLOBAL_FACE_DIR DATADIR "/faces"
+#define DEFAULT_USER_ICON       "stock_person"
+#define DEFAULT_EXCLUDE         { "bin",        \
+                                  "root",       \
+                                  "daemon",     \
+                                  "adm",        \
+                                  "lp",         \
+                                  "sync",       \
+                                  "shutdown",   \
+                                  "halt",       \
+                                  "mail",       \
+                                  "news",       \
+                                  "uucp",       \
+                                  "operator",   \
+                                  "nobody",     \
+                                  GDM_USERNAME, \
+                                  "postgres",   \
+                                  "pvm",        \
+                                  "rpm",        \
+                                  "nfsnobody",  \
+                                  "pcap",       \
+                                  NULL }
+
+struct GdmUserManagerPrivate
+{
+        GHashTable            *users;
+        GHashTable            *sessions;
+        GHashTable            *exclusions;
+        GHashTable            *shells;
+        DBusGConnection       *connection;
+        DBusGProxy            *seat_proxy;
+        char                  *seat_id;
+
+        GFileMonitor          *passwd_monitor;
+        GFileMonitor          *shells_monitor;
+
+        guint                  reload_id;
+        guint                  ck_history_id;
+
+        guint8                 users_dirty : 1;
+};
+
+enum {
+        LOADING_USERS,
+        USERS_LOADED,
+        USER_ADDED,
+        USER_REMOVED,
+        USER_IS_LOGGED_IN_CHANGED,
+        USER_LOGIN_FREQUENCY_CHANGED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void     gdm_user_manager_class_init (GdmUserManagerClass *klass);
+static void     gdm_user_manager_init       (GdmUserManager      *user_manager);
+static void     gdm_user_manager_finalize   (GObject             *object);
+
+static gpointer user_manager_object = NULL;
+
+G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
+
+GQuark
+gdm_user_manager_error_quark (void)
+{
+        static GQuark ret = 0;
+        if (ret == 0) {
+                ret = g_quark_from_static_string ("gdm_user_manager_error");
+        }
+
+        return ret;
+}
+
+static gboolean
+start_new_login_session (GdmUserManager *manager)
+{
+        GError  *error;
+        gboolean res;
+
+        res = g_spawn_command_line_async ("gdmflexiserver -s", &error);
+        if (! res) {
+                g_warning ("Unable to start new login: %s", error->message);
+                g_error_free (error);
+        }
+
+        return res;
+}
+
+/* needs to stay in sync with gdm-slave */
+static char *
+_get_primary_user_session_id (GdmUserManager *manager,
+                              GdmUser        *user)
+{
+        gboolean    res;
+        gboolean    can_activate_sessions;
+        GError     *error;
+        GList      *sessions;
+        GList      *l;
+        char       *primary_ssid;
+
+        if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
+                g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
+                return NULL;
+        }
+
+        primary_ssid = NULL;
+        sessions = NULL;
+
+        g_debug ("GdmUserManager: checking if seat can activate sessions");
+
+        error = NULL;
+        res = dbus_g_proxy_call (manager->priv->seat_proxy,
+                                 "CanActivateSessions",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 G_TYPE_BOOLEAN, &can_activate_sessions,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_warning ("unable to determine if seat can activate sessions: %s",
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        if (! can_activate_sessions) {
+                g_debug ("GdmUserManager: seat is unable to activate sessions");
+                goto out;
+        }
+
+        sessions = gdm_user_get_sessions (user);
+        if (sessions == NULL) {
+                g_warning ("unable to determine sessions for user: %s",
+                           gdm_user_get_user_name (user));
+                goto out;
+        }
+
+        for (l = sessions; l != NULL; l = l->next) {
+                const char *ssid;
+
+                ssid = l->data;
+
+                /* FIXME: better way to choose? */
+                if (ssid != NULL) {
+                        primary_ssid = g_strdup (ssid);
+                        break;
+                }
+        }
+
+ out:
+
+        return primary_ssid;
+}
+
+static gboolean
+activate_session_id (GdmUserManager *manager,
+                     const char     *seat_id,
+                     const char     *session_id)
+{
+        DBusError    local_error;
+        DBusMessage *message;
+        DBusMessage *reply;
+        gboolean     ret;
+
+        ret = FALSE;
+        reply = NULL;
+
+        dbus_error_init (&local_error);
+        message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
+                                                seat_id,
+                                                "org.freedesktop.ConsoleKit.Seat",
+                                                "ActivateSession");
+        if (message == NULL) {
+                goto out;
+        }
+
+        if (! dbus_message_append_args (message,
+                                        DBUS_TYPE_OBJECT_PATH, &session_id,
+                                        DBUS_TYPE_INVALID)) {
+                goto out;
+        }
+
+
+        dbus_error_init (&local_error);
+        reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (manager->priv->connection),
+                                                           message,
+                                                           -1,
+                                                           &local_error);
+        if (reply == NULL) {
+                if (dbus_error_is_set (&local_error)) {
+                        g_warning ("Unable to activate session: %s", local_error.message);
+                        dbus_error_free (&local_error);
+                        goto out;
+                }
+        }
+
+        ret = TRUE;
+ out:
+        if (message != NULL) {
+                dbus_message_unref (message);
+        }
+        if (reply != NULL) {
+                dbus_message_unref (reply);
+        }
+
+        return ret;
+}
+
+static gboolean
+session_is_login_window (GdmUserManager *manager,
+                         const char     *session_id)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+        gboolean         res;
+        gboolean         ret;
+        char            *session_type;
+
+        ret = FALSE;
+
+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+                                           CK_NAME,
+                                           session_id,
+                                           CK_SESSION_INTERFACE);
+        if (proxy == NULL) {
+                g_warning ("Failed to connect to the ConsoleKit seat object");
+                goto out;
+        }
+
+        session_type = NULL;
+        error = NULL;
+        res = dbus_g_proxy_call (proxy,
+                                 "GetSessionType",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 G_TYPE_STRING, &session_type,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_debug ("Failed to identify the session type: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        if (session_type == NULL || session_type[0] == '\0' || strcmp (session_type, "LoginWindow") != 0) {
+                goto out;
+        }
+
+        ret = TRUE;
+
+ out:
+        if (proxy != NULL) {
+                g_object_unref (proxy);
+        }
+
+        return ret;
+}
+
+static char *
+_get_login_window_session_id (GdmUserManager *manager)
+{
+        gboolean    res;
+        gboolean    can_activate_sessions;
+        GError     *error;
+        GPtrArray  *sessions;
+        char       *primary_ssid;
+        int         i;
+
+        if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
+                g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
+                return NULL;
+        }
+
+        primary_ssid = NULL;
+        sessions = NULL;
+
+        g_debug ("GdmSlave: checking if seat can activate sessions");
+
+        error = NULL;
+        res = dbus_g_proxy_call (manager->priv->seat_proxy,
+                                 "CanActivateSessions",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 G_TYPE_BOOLEAN, &can_activate_sessions,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_warning ("unable to determine if seat can activate sessions: %s",
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        if (! can_activate_sessions) {
+                g_debug ("GdmSlave: seat is unable to activate sessions");
+                goto out;
+        }
+
+        error = NULL;
+        res = dbus_g_proxy_call (manager->priv->seat_proxy,
+                                 "GetSessions",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_warning ("unable to determine sessions for user: %s",
+                           error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        for (i = 0; i < sessions->len; i++) {
+                char *ssid;
+
+                ssid = g_ptr_array_index (sessions, i);
+
+                if (session_is_login_window (manager, ssid)) {
+                        primary_ssid = g_strdup (ssid);
+                        break;
+                }
+        }
+        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
+        g_ptr_array_free (sessions, TRUE);
+
+ out:
+
+        return primary_ssid;
+}
+
+gboolean
+gdm_user_manager_goto_login_session (GdmUserManager *manager)
+{
+        gboolean ret;
+        gboolean res;
+        char    *ssid;
+
+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
+
+        ret = FALSE;
+
+        /* First look for any existing LoginWindow sessions on the seat.
+           If none are found, create a new one. */
+
+        ssid = _get_login_window_session_id (manager);
+        if (ssid != NULL) {
+                res = activate_session_id (manager, manager->priv->seat_id, ssid);
+                if (res) {
+                        ret = TRUE;
+                }
+        }
+
+        if (! ret) {
+                res = start_new_login_session (manager);
+                if (res) {
+                        ret = TRUE;
+                }
+        }
+
+        return ret;
+}
+
+gboolean
+gdm_user_manager_activate_user_session (GdmUserManager *manager,
+                                        GdmUser        *user)
+{
+        gboolean ret;
+        char    *ssid;
+        gboolean res;
+
+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
+        g_return_val_if_fail (GDM_IS_USER (user), FALSE);
+
+        ret = FALSE;
+
+        ssid = _get_primary_user_session_id (manager, user);
+        if (ssid == NULL) {
+                goto out;
+        }
+
+        res = activate_session_id (manager, manager->priv->seat_id, ssid);
+        if (! res) {
+                g_debug ("GdmUserManager: unable to activate session: %s", ssid);
+                goto out;
+        }
+
+        ret = TRUE;
+ out:
+        return ret;
+}
+
+static void
+on_user_sessions_changed (GdmUser        *user,
+                          GdmUserManager *manager)
+{
+        guint nsessions;
+
+        nsessions = gdm_user_get_num_sessions (user);
+
+        g_debug ("GdmUserManager: sessions changed user=%s num=%d",
+                 gdm_user_get_user_name (user),
+                 nsessions);
+
+        /* only signal on zero and one */
+        if (nsessions > 1) {
+                return;
+        }
+
+        g_signal_emit (manager, signals [USER_IS_LOGGED_IN_CHANGED], 0, user);
+}
+
+static void
+on_user_icon_changed (GdmUser        *user,
+                      GdmUserManager *manager)
+{
+        g_debug ("GdmUserManager: user icon changed");
+}
+
+static char *
+get_seat_id_for_session (DBusGConnection *connection,
+                         const char      *session_id)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+        char            *seat_id;
+        gboolean         res;
+
+        proxy = NULL;
+        seat_id = NULL;
+
+        proxy = dbus_g_proxy_new_for_name (connection,
+                                           CK_NAME,
+                                           session_id,
+                                           CK_SESSION_INTERFACE);
+        if (proxy == NULL) {
+                g_warning ("Failed to connect to the ConsoleKit session object");
+                goto out;
+        }
+
+        error = NULL;
+        res = dbus_g_proxy_call (proxy,
+                                 "GetSeatId",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 DBUS_TYPE_G_OBJECT_PATH, &seat_id,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_debug ("Failed to identify the current seat: %s", error->message);
+                g_error_free (error);
+        }
+ out:
+        if (proxy != NULL) {
+                g_object_unref (proxy);
+        }
+
+        return seat_id;
+}
+
+static char *
+get_x11_display_for_session (DBusGConnection *connection,
+                             const char      *session_id)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+        char            *x11_display;
+        gboolean         res;
+
+        proxy = NULL;
+        x11_display = NULL;
+
+        proxy = dbus_g_proxy_new_for_name (connection,
+                                           CK_NAME,
+                                           session_id,
+                                           CK_SESSION_INTERFACE);
+        if (proxy == NULL) {
+                g_warning ("Failed to connect to the ConsoleKit session object");
+                goto out;
+        }
+
+        error = NULL;
+        res = dbus_g_proxy_call (proxy,
+                                 "GetX11Display",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 G_TYPE_STRING, &x11_display,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_debug ("Failed to identify the x11 display: %s", error->message);
+                g_error_free (error);
+        }
+ out:
+        if (proxy != NULL) {
+                g_object_unref (proxy);
+        }
+
+        return x11_display;
+}
+
+static gboolean
+maybe_add_session_for_user (GdmUserManager *manager,
+                            GdmUser        *user,
+                            const char     *ssid)
+{
+        char    *sid;
+        char    *x11_display;
+        gboolean ret;
+
+        ret = FALSE;
+        sid = NULL;
+        x11_display = NULL;
+
+        /* skip if on another seat */
+        sid = get_seat_id_for_session (manager->priv->connection, ssid);
+        if (sid == NULL
+            || manager->priv->seat_id == NULL
+            || strcmp (sid, manager->priv->seat_id) != 0) {
+                g_debug ("GdmUserManager: not adding session on other seat: %s", ssid);
+                goto out;
+        }
+
+        /* skip if doesn't have an x11 display */
+        x11_display = get_x11_display_for_session (manager->priv->connection, ssid);
+        if (x11_display == NULL || x11_display[0] == '\0') {
+                g_debug ("GdmUserManager: not adding session without a x11 display: %s", ssid);
+                goto out;
+        }
+
+        if (g_hash_table_lookup (manager->priv->exclusions, gdm_user_get_user_name (user))) {
+                g_debug ("GdmUserManager: excluding user '%s'", gdm_user_get_user_name (user));
+                goto out;
+        }
+
+        g_hash_table_insert (manager->priv->sessions,
+                             g_strdup (ssid),
+                             g_strdup (gdm_user_get_user_name (user)));
+
+        _gdm_user_add_session (user, ssid);
+        g_debug ("GdmUserManager: added session for user: %s", gdm_user_get_user_name (user));
+
+        ret = TRUE;
+
+ out:
+        g_free (sid);
+        g_free (x11_display);
+
+        return ret;
+}
+
+static void
+add_sessions_for_user (GdmUserManager *manager,
+                       GdmUser        *user)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+        gboolean         res;
+        guint32          uid;
+        GPtrArray       *sessions;
+        int              i;
+
+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+                                           CK_NAME,
+                                           CK_MANAGER_PATH,
+                                           CK_MANAGER_INTERFACE);
+        if (proxy == NULL) {
+                g_warning ("Failed to connect to the ConsoleKit manager object");
+                goto out;
+        }
+
+        uid = gdm_user_get_uid (user);
+
+        g_debug ("Getting list of sessions for user %u", uid);
+
+        error = NULL;
+        res = dbus_g_proxy_call (proxy,
+                                 "GetSessionsForUnixUser",
+                                 &error,
+                                 G_TYPE_UINT, uid,
+                                 G_TYPE_INVALID,
+                                 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
+                                 &sessions,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_debug ("Failed to find sessions for user: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        g_debug ("Found %d sessions for user %s", sessions->len, gdm_user_get_user_name (user));
+
+        for (i = 0; i < sessions->len; i++) {
+                char *ssid;
+
+                ssid = g_ptr_array_index (sessions, i);
+                maybe_add_session_for_user (manager, user, ssid);
+        }
+
+        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
+        g_ptr_array_free (sessions, TRUE);
+
+ out:
+        if (proxy != NULL) {
+                g_object_unref (proxy);
+        }
+}
+
+static GdmUser *
+create_user (GdmUserManager *manager)
+{
+        GdmUser *user;
+
+        user = g_object_new (GDM_TYPE_USER, "manager", manager, NULL);
+        g_signal_connect (user,
+                          "sessions-changed",
+                          G_CALLBACK (on_user_sessions_changed),
+                          manager);
+        g_signal_connect (user,
+                          "icon-changed",
+                          G_CALLBACK (on_user_icon_changed),
+                          manager);
+        return user;
+}
+
+static void
+add_user (GdmUserManager *manager,
+          GdmUser        *user)
+{
+        add_sessions_for_user (manager, user);
+        g_hash_table_insert (manager->priv->users,
+                             g_strdup (gdm_user_get_user_name (user)),
+                             g_object_ref (user));
+
+        g_signal_emit (manager, signals[USER_ADDED], 0, user);
+}
+
+static GdmUser *
+add_new_user_for_pwent (GdmUserManager *manager,
+                        struct passwd  *pwent)
+{
+        GdmUser *user;
+
+        g_debug ("Creating new user");
+
+        user = create_user (manager);
+        _gdm_user_update (user, pwent);
+
+        add_user (manager, user);
+
+        return user;
+}
+
+static char *
+get_current_seat_id (DBusGConnection *connection)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+        char            *session_id;
+        char            *seat_id;
+        gboolean         res;
+
+        proxy = NULL;
+        session_id = NULL;
+        seat_id = NULL;
+
+        proxy = dbus_g_proxy_new_for_name (connection,
+                                           CK_NAME,
+                                           CK_MANAGER_PATH,
+                                           CK_MANAGER_INTERFACE);
+        if (proxy == NULL) {
+                g_warning ("Failed to connect to the ConsoleKit manager object");
+                goto out;
+        }
+
+        error = NULL;
+        res = dbus_g_proxy_call (proxy,
+                                 "GetCurrentSession",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 DBUS_TYPE_G_OBJECT_PATH,
+                                 &session_id,
+                                 G_TYPE_INVALID);
+        if (! res) {
+                g_debug ("Failed to identify the current session: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        seat_id = get_seat_id_for_session (connection, session_id);
+
+ out:
+        if (proxy != NULL) {
+                g_object_unref (proxy);
+        }
+        g_free (session_id);
+
+        return seat_id;
+}
+
+static gboolean
+get_uid_from_session_id (GdmUserManager *manager,
+                         const char     *session_id,
+                         uid_t          *uidp)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+        guint            uid;
+        gboolean         res;
+
+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+                                           CK_NAME,
+                                           session_id,
+                                           CK_SESSION_INTERFACE);
+        if (proxy == NULL) {
+                g_warning ("Failed to connect to the ConsoleKit session object");
+                return FALSE;
+        }
+
+        error = NULL;
+        res = dbus_g_proxy_call (proxy,
+                                 "GetUnixUser",
+                                 &error,
+                                 G_TYPE_INVALID,
+                                 G_TYPE_UINT, &uid,
+                                 G_TYPE_INVALID);
+        g_object_unref (proxy);
+
+        if (! res) {
+                g_warning ("Failed to query the session: %s", error->message);
+                g_error_free (error);
+                return FALSE;
+        }
+
+        if (uidp != NULL) {
+                *uidp = (uid_t) uid;
+        }
+
+        return TRUE;
+}
+
+static void
+seat_session_added (DBusGProxy     *seat_proxy,
+                    const char     *session_id,
+                    GdmUserManager *manager)
+{
+        uid_t          uid;
+        gboolean       res;
+        struct passwd *pwent;
+        GdmUser       *user;
+        gboolean       is_new;
+
+        g_debug ("Session added: %s", session_id);
+
+        res = get_uid_from_session_id (manager, session_id, &uid);
+        if (! res) {
+                g_warning ("Unable to lookup user for session");
+                return;
+        }
+
+        errno = 0;
+        pwent = getpwuid (uid);
+        if (pwent == NULL) {
+                g_warning ("Unable to lookup user id %d: %s", (int)uid, g_strerror (errno));
+                return;
+        }
+
+        /* check exclusions up front */
+        if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
+                g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
+                return;
+        }
+
+        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+        if (user == NULL) {
+                g_debug ("Creating new user");
+
+                user = create_user (manager);
+                _gdm_user_update (user, pwent);
+                is_new = TRUE;
+        } else {
+                is_new = FALSE;
+        }
+
+        res = maybe_add_session_for_user (manager, user, session_id);
+
+        /* only add the user if we added a session */
+        if (is_new) {
+                if (res) {
+                        add_user (manager, user);
+                } else {
+                        g_object_unref (user);
+                }
+        }
+}
+
+static void
+seat_session_removed (DBusGProxy     *seat_proxy,
+                      const char     *session_id,
+                      GdmUserManager *manager)
+{
+        GdmUser *user;
+        char    *username;
+
+        g_debug ("Session removed: %s", session_id);
+
+        /* since the session object may already be gone
+         * we can't query CK directly */
+
+        username = g_hash_table_lookup (manager->priv->sessions, session_id);
+        if (username == NULL) {
+                return;
+        }
+
+        user = g_hash_table_lookup (manager->priv->users, username);
+        if (user == NULL) {
+                /* nothing to do */
+                return;
+        }
+
+        g_debug ("GdmUserManager: Session removed for %s", username);
+        _gdm_user_remove_session (user, session_id);
+}
+
+static void
+on_proxy_destroy (DBusGProxy     *proxy,
+                  GdmUserManager *manager)
+{
+        g_debug ("GdmUserManager: seat proxy destroyed");
+
+        manager->priv->seat_proxy = NULL;
+}
+
+static void
+get_seat_proxy (GdmUserManager *manager)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+
+        g_assert (manager->priv->seat_proxy == NULL);
+
+        error = NULL;
+        manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+        if (manager->priv->connection == NULL) {
+                g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        manager->priv->seat_id = get_current_seat_id (manager->priv->connection);
+        if (manager->priv->seat_id == NULL) {
+                return;
+        }
+
+        g_debug ("GdmUserManager: Found current seat: %s", manager->priv->seat_id);
+
+        error = NULL;
+        proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
+                                                 CK_NAME,
+                                                 manager->priv->seat_id,
+                                                 CK_SEAT_INTERFACE,
+                                                 &error);
+
+        if (proxy == NULL) {
+                g_warning ("Failed to connect to the ConsoleKit seat object: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        g_signal_connect (proxy, "destroy", G_CALLBACK (on_proxy_destroy), manager);
+
+        dbus_g_proxy_add_signal (proxy,
+                                 "SessionAdded",
+                                 DBUS_TYPE_G_OBJECT_PATH,
+                                 G_TYPE_INVALID);
+        dbus_g_proxy_add_signal (proxy,
+                                 "SessionRemoved",
+                                 DBUS_TYPE_G_OBJECT_PATH,
+                                 G_TYPE_INVALID);
+        dbus_g_proxy_connect_signal (proxy,
+                                     "SessionAdded",
+                                     G_CALLBACK (seat_session_added),
+                                     manager,
+                                     NULL);
+        dbus_g_proxy_connect_signal (proxy,
+                                     "SessionRemoved",
+                                     G_CALLBACK (seat_session_removed),
+                                     manager,
+                                     NULL);
+        manager->priv->seat_proxy = proxy;
+
+}
+
+/**
+ * gdm_manager_get_user:
+ * @manager: the manager to query.
+ * @username: the login name of the user to get.
+ *
+ * Retrieves a pointer to the #GdmUser object for the login named @username
+ * from @manager. This pointer is not a reference, and should not be released.
+ *
+ * Returns: a pointer to a #GdmUser object.
+ **/
+GdmUser *
+gdm_user_manager_get_user (GdmUserManager *manager,
+                           const char     *username)
+{
+        GdmUser *user;
+
+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+        g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
+
+        user = g_hash_table_lookup (manager->priv->users, username);
+
+        if (user == NULL) {
+                struct passwd *pwent;
+
+                pwent = getpwnam (username);
+
+                if (pwent != NULL) {
+                        user = add_new_user_for_pwent (manager, pwent);
+                }
+        }
+
+        return user;
+}
+
+GdmUser *
+gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
+                                  uid_t           uid)
+{
+        GdmUser       *user;
+        struct passwd *pwent;
+
+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+
+        pwent = getpwuid (uid);
+        if (pwent == NULL) {
+                g_warning ("GdmUserManager: unable to lookup uid %d", (int)uid);
+                return NULL;
+        }
+
+        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+
+        if (user == NULL) {
+                user = add_new_user_for_pwent (manager, pwent);
+        }
+
+        return user;
+}
+
+static void
+listify_hash_values_hfunc (gpointer key,
+                           gpointer value,
+                           gpointer user_data)
+{
+        GSList **list = user_data;
+
+        *list = g_slist_prepend (*list, value);
+}
+
+GSList *
+gdm_user_manager_list_users (GdmUserManager *manager)
+{
+        GSList *retval;
+
+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+
+        retval = NULL;
+        g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &retval);
+
+        return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
+}
+
+static gboolean
+parse_value_as_ulong (const char *value,
+                      gulong     *ulongval)
+{
+        char  *end_of_valid_long;
+        glong  long_value;
+        gulong ulong_value;
+
+        errno = 0;
+        long_value = strtol (value, &end_of_valid_long, 10);
+
+        if (*value == '\0' || *end_of_valid_long != '\0') {
+                return FALSE;
+        }
+
+        ulong_value = long_value;
+        if (ulong_value != long_value || errno == ERANGE) {
+                return FALSE;
+        }
+
+        *ulongval = ulong_value;
+
+        return TRUE;
+}
+
+static gboolean
+parse_ck_history_line (const char *line,
+                       char      **user_namep,
+                       gulong     *frequencyp)
+{
+        GRegex     *re;
+        GMatchInfo *match_info;
+        gboolean    res;
+        gboolean    ret;
+        GError     *error;
+
+        ret = FALSE;
+        re = NULL;
+        match_info = NULL;
+
+        error = NULL;
+        re = g_regex_new ("(?P<username>[0-9a-zA-Z]+)[ ]+(?P<frequency>[0-9]+)", 0, 0, &error);
+        if (re == NULL) {
+                g_critical ("%s", error->message);
+                goto out;
+        }
+
+        g_regex_match (re, line, 0, &match_info);
+
+        res = g_match_info_matches (match_info);
+        if (! res) {
+                g_warning ("Unable to parse history: %s", line);
+                goto out;
+        }
+
+        if (user_namep != NULL) {
+                *user_namep = g_match_info_fetch_named (match_info, "username");
+        }
+
+        if (frequencyp != NULL) {
+                char *freq;
+                freq = g_match_info_fetch_named (match_info, "frequency");
+                res = parse_value_as_ulong (freq, frequencyp);
+                g_free (freq);
+                if (! res) {
+                        goto out;
+                }
+        }
+
+        ret = TRUE;
+
+ out:
+        if (match_info != NULL) {
+                g_match_info_free (match_info);
+        }
+        if (re != NULL) {
+                g_regex_unref (re);
+        }
+        return ret;
+}
+
+static void
+process_ck_history_line (GdmUserManager *manager,
+                         const char     *line)
+{
+        gboolean res;
+        char    *username;
+        gulong   frequency;
+        struct passwd *pwent;
+        GdmUser *user;
+
+        frequency = 0;
+        username = NULL;
+        res = parse_ck_history_line (line, &username, &frequency);
+        if (! res) {
+                return;
+        }
+
+        if (g_hash_table_lookup (manager->priv->exclusions, username)) {
+                g_debug ("GdmUserManager: excluding user '%s'", username);
+                g_free (username);
+                return;
+        }
+
+        /* https://bugzilla.gnome.org/show_bug.cgi?id=587708 */
+        /* do not show system users; we cannot use gdm_user_manager_get_user()
+         * here since this creates/signals users as a side effect */
+        pwent = getpwnam (username);
+        if (pwent == NULL) {
+                g_warning ("Unable to lookup user name %s: %s", username, g_strerror (errno));
+                return;
+        }
+        if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
+                g_debug ("GdmUserManager: excluding user '%s'", username);
+                return;
+        }
+
+        user = gdm_user_manager_get_user (manager, username);
+        if (user == NULL) {
+                g_debug ("GdmUserManager: unable to lookup user '%s'", username);
+                g_free (username);
+                return;
+        }
+
+        g_object_set (user, "login-frequency", frequency, NULL);
+        g_signal_emit (manager, signals [USER_LOGIN_FREQUENCY_CHANGED], 0, user);
+        g_free (username);
+}
+
+static gboolean
+ck_history_watch (GIOChannel     *source,
+                  GIOCondition    condition,
+                  GdmUserManager *manager)
+{
+        GIOStatus status;
+        gboolean  done  = FALSE;
+
+        g_return_val_if_fail (manager != NULL, FALSE);
+
+        if (condition & G_IO_IN) {
+                char   *str;
+                GError *error;
+
+                error = NULL;
+                status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
+                if (error != NULL) {
+                        g_warning ("GdmUserManager: unable to read line: %s", error->message);
+                        g_error_free (error);
+                }
+
+                if (status == G_IO_STATUS_NORMAL) {
+                        g_debug ("GdmUserManager: history output: %s", str);
+                        process_ck_history_line (manager, str);
+                } else if (status == G_IO_STATUS_EOF) {
+                        done = TRUE;
+                }
+
+                g_free (str);
+        } else if (condition & G_IO_HUP) {
+                done = TRUE;
+        }
+
+        if (done) {
+                g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
+
+                manager->priv->ck_history_id = 0;
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static void
+reload_ck_history (GdmUserManager *manager)
+{
+        char       *command;
+        const char *seat_id;
+        GError     *error;
+        gboolean    res;
+        char      **argv;
+        int         standard_out;
+        GIOChannel *channel;
+
+        seat_id = NULL;
+        if (manager->priv->seat_id != NULL
+            && g_str_has_prefix (manager->priv->seat_id, "/org/freedesktop/ConsoleKit/")) {
+
+                seat_id = manager->priv->seat_id + strlen ("/org/freedesktop/ConsoleKit/");
+        }
+
+        if (seat_id == NULL) {
+                g_warning ("Unable to find users: no seat-id found");
+                return;
+        }
+
+        command = g_strdup_printf ("ck-history --frequent --seat='%s' --session-type=''",
+                                   seat_id);
+        g_debug ("GdmUserManager: running '%s'", command);
+        error = NULL;
+        if (! g_shell_parse_argv (command, NULL, &argv, &error)) {
+                g_warning ("Could not parse command: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        error = NULL;
+        res = g_spawn_async_with_pipes (NULL,
+                                        argv,
+                                        NULL,
+                                        G_SPAWN_SEARCH_PATH,
+                                        NULL,
+                                        NULL,
+                                        NULL, /* pid */
+                                        NULL,
+                                        &standard_out,
+                                        NULL,
+                                        &error);
+        g_strfreev (argv);
+        if (! res) {
+                g_warning ("Unable to run ck-history: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        channel = g_io_channel_unix_new (standard_out);
+        g_io_channel_set_close_on_unref (channel, TRUE);
+        g_io_channel_set_flags (channel,
+                                g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
+                                NULL);
+        manager->priv->ck_history_id = g_io_add_watch (channel,
+                                                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+                                                       (GIOFunc)ck_history_watch,
+                                                       manager);
+        g_io_channel_unref (channel);
+
+ out:
+        g_free (command);
+}
+
+static void
+reload_passwd (GdmUserManager *manager)
+{
+        struct passwd *pwent;
+        GSList        *old_users;
+        GSList        *new_users;
+        GSList        *list;
+        FILE          *fp;
+
+        old_users = NULL;
+        new_users = NULL;
+
+        errno = 0;
+        fp = fopen (PATH_PASSWD, "r");
+        if (fp == NULL) {
+                g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
+                goto out;
+        }
+
+        g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &old_users);
+        g_slist_foreach (old_users, (GFunc) g_object_ref, NULL);
+
+        /* Make sure we keep users who are logged in no matter what. */
+        for (list = old_users; list; list = list->next) {
+                if (gdm_user_get_num_sessions (list->data) > 0) {
+                        g_object_freeze_notify (G_OBJECT (list->data));
+                        new_users = g_slist_prepend (new_users, g_object_ref (list->data));
+                }
+        }
+
+        for (pwent = fgetpwent (fp); pwent != NULL; pwent = fgetpwent (fp)) {
+                GdmUser *user;
+
+                user = NULL;
+
+                /* Skip users below MinimalUID... */
+                if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
+                        continue;
+                }
+
+                /* ...And users w/ invalid shells... */
+                if (pwent->pw_shell == NULL ||
+                    !g_hash_table_lookup (manager->priv->shells, pwent->pw_shell)) {
+                        g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
+                        continue;
+                }
+
+                /* ...And explicitly excluded users */
+                if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
+                        g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
+                        continue;
+                }
+
+                user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+
+                /* Update users already in the *new* list */
+                if (g_slist_find (new_users, user)) {
+                        _gdm_user_update (user, pwent);
+                        continue;
+                }
+
+                if (user == NULL) {
+                        user = create_user (manager);
+                } else {
+                        g_object_ref (user);
+                }
+
+                /* Freeze & update users not already in the new list */
+                g_object_freeze_notify (G_OBJECT (user));
+                _gdm_user_update (user, pwent);
+
+                new_users = g_slist_prepend (new_users, user);
+        }
+
+        /* Go through and handle added users */
+        for (list = new_users; list; list = list->next) {
+                if (! g_slist_find (old_users, list->data)) {
+                        add_user (manager, list->data);
+                }
+        }
+
+        /* Go through and handle removed users */
+        for (list = old_users; list; list = list->next) {
+                if (! g_slist_find (new_users, list->data)) {
+                        g_signal_emit (manager, signals[USER_REMOVED], 0, list->data);
+                        g_hash_table_remove (manager->priv->users,
+                                             gdm_user_get_user_name (list->data));
+                }
+        }
+
+ out:
+        /* Cleanup */
+
+        fclose (fp);
+
+        g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL);
+        g_slist_foreach (new_users, (GFunc) g_object_unref, NULL);
+        g_slist_free (new_users);
+
+        g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
+        g_slist_free (old_users);
+}
+
+static void
+reload_users (GdmUserManager *manager)
+{
+        reload_ck_history (manager);
+        reload_passwd (manager);
+}
+
+static gboolean
+reload_users_timeout (GdmUserManager *manager)
+{
+        reload_users (manager);
+        manager->priv->reload_id = 0;
+
+        return FALSE;
+}
+
+static void
+queue_reload_users (GdmUserManager *manager)
+{
+        if (manager->priv->reload_id > 0) {
+                return;
+        }
+
+        g_signal_emit (G_OBJECT (manager), signals[LOADING_USERS], 0);
+        manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, manager);
+}
+
+static void
+reload_shells (GdmUserManager *manager)
+{
+        char *shell;
+
+        setusershell ();
+
+        g_hash_table_remove_all (manager->priv->shells);
+        for (shell = getusershell (); shell != NULL; shell = getusershell ()) {
+                /* skip well known not-real shells */
+                if (shell == NULL
+                    || strcmp (shell, "/sbin/nologin") == 0
+                    || strcmp (shell, "/bin/false") == 0) {
+                        g_debug ("GdmUserManager: skipping shell %s", shell);
+                        continue;
+                }
+                g_hash_table_insert (manager->priv->shells,
+                                     g_strdup (shell),
+                                     GUINT_TO_POINTER (TRUE));
+        }
+
+        endusershell ();
+}
+
+static void
+on_shells_monitor_changed (GFileMonitor     *monitor,
+                           GFile            *file,
+                           GFile            *other_file,
+                           GFileMonitorEvent event_type,
+                           GdmUserManager   *manager)
+{
+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
+                return;
+        }
+
+        reload_shells (manager);
+        reload_passwd (manager);
+}
+
+static void
+on_passwd_monitor_changed (GFileMonitor     *monitor,
+                           GFile            *file,
+                           GFile            *other_file,
+                           GFileMonitorEvent event_type,
+                           GdmUserManager   *manager)
+{
+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
+                return;
+        }
+
+        reload_passwd (manager);
+}
+
+static void
+gdm_user_manager_class_init (GdmUserManagerClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gdm_user_manager_finalize;
+
+        signals [LOADING_USERS] =
+                g_signal_new ("loading-users",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserManagerClass, loading_users),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [USERS_LOADED] =
+                g_signal_new ("users-loaded",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserManagerClass, users_loaded),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [USER_ADDED] =
+                g_signal_new ("user-added",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_added),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
+        signals [USER_REMOVED] =
+                g_signal_new ("user-removed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_removed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
+        signals [USER_IS_LOGGED_IN_CHANGED] =
+                g_signal_new ("user-is-logged-in-changed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_is_logged_in_changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
+        signals [USER_LOGIN_FREQUENCY_CHANGED] =
+                g_signal_new ("user-login-frequency-changed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_login_frequency_changed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
+
+        g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
+}
+
+static void
+gdm_user_manager_init (GdmUserManager *manager)
+{
+        int            i;
+        GFile         *file;
+        GError        *error;
+        const char    *exclude_default[] = DEFAULT_EXCLUDE;
+
+        manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
+
+        /* sessions */
+        manager->priv->sessions = g_hash_table_new_full (g_str_hash,
+                                                         g_str_equal,
+                                                         g_free,
+                                                         g_free);
+
+        /* exclusions */
+        manager->priv->exclusions = g_hash_table_new_full (g_str_hash,
+                                                           g_str_equal,
+                                                           g_free,
+                                                           NULL);
+        for (i = 0; exclude_default[i] != NULL; i++) {
+                g_hash_table_insert (manager->priv->exclusions,
+                                     g_strdup (exclude_default [i]),
+                                     GUINT_TO_POINTER (TRUE));
+        }
+
+        /* /etc/shells */
+        manager->priv->shells = g_hash_table_new_full (g_str_hash,
+                                                       g_str_equal,
+                                                       g_free,
+                                                       NULL);
+        reload_shells (manager);
+        file = g_file_new_for_path (_PATH_SHELLS);
+        error = NULL;
+        manager->priv->shells_monitor = g_file_monitor_file (file,
+                                                             G_FILE_MONITOR_NONE,
+                                                             NULL,
+                                                             &error);
+        if (manager->priv->shells_monitor != NULL) {
+                g_signal_connect (manager->priv->shells_monitor,
+                                  "changed",
+                                  G_CALLBACK (on_shells_monitor_changed),
+                                  manager);
+        } else {
+                g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
+                g_error_free (error);
+        }
+        g_object_unref (file);
+
+        /* /etc/passwd */
+        manager->priv->users = g_hash_table_new_full (g_str_hash,
+                                                      g_str_equal,
+                                                      g_free,
+                                                      (GDestroyNotify) g_object_run_dispose);
+        file = g_file_new_for_path (PATH_PASSWD);
+        manager->priv->passwd_monitor = g_file_monitor_file (file,
+                                                             G_FILE_MONITOR_NONE,
+                                                             NULL,
+                                                             &error);
+        if (manager->priv->passwd_monitor != NULL) {
+                g_signal_connect (manager->priv->passwd_monitor,
+                                  "changed",
+                                  G_CALLBACK (on_passwd_monitor_changed),
+                                  manager);
+        } else {
+                g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
+                g_error_free (error);
+        }
+        g_object_unref (file);
+
+
+        get_seat_proxy (manager);
+
+        queue_reload_users (manager);
+
+        manager->priv->users_dirty = FALSE;
+}
+
+static void
+gdm_user_manager_finalize (GObject *object)
+{
+        GdmUserManager *manager;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDM_IS_USER_MANAGER (object));
+
+        manager = GDM_USER_MANAGER (object);
+
+        g_return_if_fail (manager->priv != NULL);
+
+        if (manager->priv->seat_proxy != NULL) {
+                g_object_unref (manager->priv->seat_proxy);
+        }
+
+        if (manager->priv->ck_history_id != 0) {
+                g_source_remove (manager->priv->ck_history_id);
+                manager->priv->ck_history_id = 0;
+        }
+
+        if (manager->priv->reload_id > 0) {
+                g_source_remove (manager->priv->reload_id);
+                manager->priv->reload_id = 0;
+        }
+
+        g_hash_table_destroy (manager->priv->sessions);
+
+        g_file_monitor_cancel (manager->priv->passwd_monitor);
+        g_hash_table_destroy (manager->priv->users);
+
+        g_file_monitor_cancel (manager->priv->shells_monitor);
+        g_hash_table_destroy (manager->priv->shells);
+
+        g_free (manager->priv->seat_id);
+
+        G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
+}
+
+GdmUserManager *
+gdm_user_manager_ref_default (void)
+{
+        if (user_manager_object != NULL) {
+                g_object_ref (user_manager_object);
+        } else {
+                user_manager_object = g_object_new (GDM_TYPE_USER_MANAGER, NULL);
+                g_object_add_weak_pointer (user_manager_object,
+                                           (gpointer *) &user_manager_object);
+        }
+
+        return GDM_USER_MANAGER (user_manager_object);
+}
Index: gdm-2.30.4/gui/gdmsetup/gdm-user-manager.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdm-user-manager.h	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * 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.
+ *
+ */
+
+#ifndef __GDM_USER_MANAGER_H
+#define __GDM_USER_MANAGER_H
+
+#include <glib-object.h>
+
+#include "gdm-user.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER_MANAGER         (gdm_user_manager_get_type ())
+#define GDM_USER_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_MANAGER, GdmUserManager))
+#define GDM_USER_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
+#define GDM_IS_USER_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_MANAGER))
+#define GDM_IS_USER_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_MANAGER))
+#define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
+
+typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
+
+typedef struct
+{
+        GObject                parent;
+        GdmUserManagerPrivate *priv;
+} GdmUserManager;
+
+typedef struct
+{
+        GObjectClass   parent_class;
+
+        void          (* loading_users)             (GdmUserManager *user_manager);
+        void          (* users_loaded)              (GdmUserManager *user_manager);
+        void          (* user_added)                (GdmUserManager *user_manager,
+                                                     GdmUser        *user);
+        void          (* user_removed)              (GdmUserManager *user_manager,
+                                                     GdmUser        *user);
+        void          (* user_is_logged_in_changed) (GdmUserManager *user_manager,
+                                                     GdmUser        *user);
+        void          (* user_login_frequency_changed) (GdmUserManager *user_manager,
+                                                        GdmUser        *user);
+} GdmUserManagerClass;
+
+typedef enum
+{
+        GDM_USER_MANAGER_ERROR_GENERAL,
+        GDM_USER_MANAGER_ERROR_KEY_NOT_FOUND
+} GdmUserManagerError;
+
+#define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
+
+GQuark              gdm_user_manager_error_quark           (void);
+GType               gdm_user_manager_get_type              (void);
+
+GdmUserManager *    gdm_user_manager_ref_default           (void);
+
+GSList *            gdm_user_manager_list_users            (GdmUserManager *manager);
+GdmUser *           gdm_user_manager_get_user              (GdmUserManager *manager,
+                                                            const char     *user_name);
+GdmUser *           gdm_user_manager_get_user_by_uid       (GdmUserManager *manager,
+                                                            uid_t           uid);
+
+gboolean            gdm_user_manager_activate_user_session (GdmUserManager *manager,
+                                                            GdmUser        *user);
+
+gboolean            gdm_user_manager_goto_login_session    (GdmUserManager *manager);
+
+G_END_DECLS
+
+#endif /* __GDM_USER_MANAGER_H */
Index: gdm-2.30.4/gui/gdmsetup/gdm-user-private.h
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/gdm-user-private.h	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,44 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+ *
+ * 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
+ */
+
+/*
+ * Private interfaces to the GdmUser object
+ */
+
+#ifndef __GDM_USER_PRIVATE__
+#define __GDM_USER_PRIVATE__ 1
+
+#include <pwd.h>
+
+#include "gdm-user.h"
+
+G_BEGIN_DECLS
+
+void _gdm_user_update           (GdmUser             *user,
+                                 const struct passwd *pwent);
+void _gdm_user_add_session      (GdmUser             *user,
+                                 const char          *session_id);
+void _gdm_user_remove_session   (GdmUser             *user,
+                                 const char          *session_id);
+
+void _gdm_user_icon_changed     (GdmUser             *user);
+
+G_END_DECLS
+
+#endif /* !__GDM_USER_PRIVATE__ */
Index: gdm-2.30.4/gui/gdmsetup/Makefile.am
===================================================================
--- /dev/null	1970-01-01 00:00:00.000000000 +0000
+++ gdm-2.30.4/gui/gdmsetup/Makefile.am	2010-08-06 12:58:16.000000000 +0200
@@ -0,0 +1,40 @@
+NULL =
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/common		\
+	-DDMCONFDIR=\""$(dmconfdir)"\"                  \
+	-DDATADIR=\""$(datadir)"\"	\
+	-DUIDIR=\""$(uidir)"\" 	\
+	-DGNOMELOCALEDIR=\""$(gdmlocaledir)"\" 	\
+	$(GDMSETUP_CFLAGS)		\
+	$(NULL)
+
+bin_PROGRAMS =			\
+	gdmsetup		\
+	$(NULL)
+
+gdmsetup_SOURCES = 		\
+	gdmsetup.c 		\
+	gdm-user.c 		\
+	gdm-user-manager.c	\
+	gdm-sessions.c           \
+	$(NULL)
+
+gdmsetup_LDADD =		\
+	$(GDMSETUP_LIBS)	\
+	$(NULL)
+        
+uidir = $(pkgdatadir)
+ui_DATA = 			\
+	gdmsetup.ui		\
+	$(NULL)
+
+Utilitiesdir = $(datadir)/applications
+Utilities_in_files = gdmsetup.desktop.in
+Utilities_DATA = $(Utilities_in_files:.desktop.in=.desktop)
+@INTLTOOL_DESKTOP_RULE@
+
+EXTRA_DIST = 			\
+	$(ui_DATA)		\
+	$(Utilities_in_files)	\
+	$(NULL)
Index: gdm-2.30.4/po/POTFILES.in
===================================================================
--- gdm-2.30.4.orig/po/POTFILES.in	2010-08-06 12:58:12.199013001 +0200
+++ gdm-2.30.4/po/POTFILES.in	2010-08-06 12:58:16.999012997 +0200
@@ -60,6 +60,9 @@
 data/greeter-autostart/metacity.desktop.in
 data/greeter-autostart/orca-screen-reader.desktop.in
 data/greeter-autostart/polkit-gnome-authentication-agent-1.desktop.in.in
+gui/gdmsetup/gdmsetup.desktop.in
+gui/gdmsetup/gdmsetup.c
+[type: gettext/glade]gui/gdmsetup/gdmsetup.ui
 gui/simple-chooser/gdm-host-chooser-dialog.c
 gui/simple-chooser/gdm-host-chooser-widget.c
 gui/simple-greeter/gdm-cell-renderer-timer.c
Index: gdm-2.30.4/gui/Makefile.am
===================================================================
--- gdm-2.30.4.orig/gui/Makefile.am	2010-06-26 00:09:31.000000000 +0200
+++ gdm-2.30.4/gui/Makefile.am	2010-08-06 12:58:16.999012997 +0200
@@ -3,6 +3,7 @@
 SUBDIRS =			\
 	simple-greeter		\
 	user-switch-applet	\
+	gdmsetup		\
 	$(NULL)
 
 if XDMCP_SUPPORT