[gnome-shell] gdm: resync cut-and-paste code from gdm tree.



commit 7ce5ea4142949745a3b3a805a099b8de9d5ed5f4
Author: Ray Strode <rstrode redhat com>
Date:   Thu Oct 7 16:59:03 2010 -0400

    gdm: resync cut-and-paste code from gdm tree.
    
    The GDM code upstream talks to the account service now,
    has better introspection annotations, and is more
    asynchronous.
    
    This commit updates the shell's copy to the latest
    upstream.
    
    Note, the API changed somewhat and so the callers will
    need to be fixed up subsequently.
    
    https://bugzilla.gnome.org/show_bug.cgi?id=631888

 src/Makefile-gdmuser.am        |    1 +
 src/gdmuser/gdm-user-manager.c | 2640 ++++++++++++++++++++++++++++++----------
 src/gdmuser/gdm-user-manager.h |   28 +-
 src/gdmuser/gdm-user-private.h |   19 +-
 src/gdmuser/gdm-user.c         | 1113 ++++++++---------
 src/gdmuser/gdm-user.h         |   18 +-
 6 files changed, 2566 insertions(+), 1253 deletions(-)
---
diff --git a/src/Makefile-gdmuser.am b/src/Makefile-gdmuser.am
index caba0d8..84563b8 100644
--- a/src/Makefile-gdmuser.am
+++ b/src/Makefile-gdmuser.am
@@ -5,6 +5,7 @@ gdmuser_cflags =				\
 	-DDATADIR=\""$(datadir)"\"		\
 	-DG_DISABLE_DEPRECATED			\
 	-DG_LOG_DOMAIN=\"GdmUser\"		\
+	-DGDM_CACHE_DIR=\""$(localstatedir)/cache/gdm"\"	\
 	$(GDMUSER_CFLAGS)			\
 	$(NULL)
 
diff --git a/src/gdmuser/gdm-user-manager.c b/src/gdmuser/gdm-user-manager.c
index 5f6bf85..b28ca20 100644
--- a/src/gdmuser/gdm-user-manager.c
+++ b/src/gdmuser/gdm-user-manager.c
@@ -51,23 +51,20 @@
 #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"
 
+#define GDM_DBUS_TYPE_G_OBJECT_PATH_ARRAY (dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH))
+
 /* 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
+#define FALLBACK_MINIMAL_UID     100
 #else
-#define DEFAULT_MINIMAL_UID     500
+#define FALLBACK_MINIMAL_UID     500
 #endif
 
 #ifndef _PATH_SHELLS
@@ -75,55 +72,134 @@
 #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",        \
-                                  "postgres",   \
-                                  "pvm",        \
-                                  "rpm",        \
-                                  "nfsnobody",  \
-                                  "pcap",       \
-                                  NULL }
+#ifndef GDM_USERNAME
+#define GDM_USERNAME "gdm"
+#endif
+
+#define RELOAD_PASSWD_THROTTLE_SECS 5
+
+/* approximately two months */
+#define LOGIN_FREQUENCY_TIME_WINDOW_SECS (60 * 24 * 60 * 60)
+
+#define ACCOUNTS_NAME      "org.freedesktop.Accounts"
+#define ACCOUNTS_PATH      "/org/freedesktop/Accounts"
+#define ACCOUNTS_INTERFACE "org.freedesktop.Accounts"
+
+typedef enum {
+        GDM_USER_MANAGER_SEAT_STATE_UNLOADED = 0,
+        GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID,
+        GDM_USER_MANAGER_SEAT_STATE_GET_ID,
+        GDM_USER_MANAGER_SEAT_STATE_GET_PROXY,
+        GDM_USER_MANAGER_SEAT_STATE_LOADED,
+} GdmUserManagerSeatState;
+
+typedef struct
+{
+        GdmUserManagerSeatState      state;
+        char                        *id;
+        char                        *session_id;
+        union {
+                DBusGProxyCall      *get_current_session_call;
+                DBusGProxyCall      *get_seat_id_call;
+        };
+
+        DBusGProxy                  *proxy;
+} GdmUserManagerSeat;
+
+typedef enum {
+        GDM_USER_MANAGER_NEW_SESSION_STATE_UNLOADED = 0,
+        GDM_USER_MANAGER_NEW_SESSION_STATE_GET_PROXY,
+        GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID,
+        GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY,
+        GDM_USER_MANAGER_NEW_SESSION_STATE_MAYBE_ADD,
+        GDM_USER_MANAGER_NEW_SESSION_STATE_LOADED,
+} GdmUserManagerNewSessionState;
+
+typedef struct
+{
+        GdmUserManager                  *manager;
+        GdmUserManagerNewSessionState    state;
+        char                            *id;
+
+        union {
+                DBusGProxyCall          *get_unix_user_call;
+                DBusGProxyCall          *get_x11_display_call;
+        };
+
+        DBusGProxy                      *proxy;
+
+        uid_t                            uid;
+        char                            *x11_display;
+} GdmUserManagerNewSession;
+
+typedef enum {
+        GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED = 0,
+        GDM_USER_MANAGER_GET_USER_STATE_WAIT_FOR_LOADED,
+        GDM_USER_MANAGER_GET_USER_STATE_ASK_ACCOUNTS_SERVICE,
+        GDM_USER_MANAGER_GET_USER_STATE_FETCHED
+} GdmUserManagerGetUserState;
+
+typedef struct
+{
+        GdmUserManager             *manager;
+        GdmUserManagerGetUserState  state;
+        GdmUser                    *user;
+        char                       *username;
+        char                       *object_path;
+
+        DBusGProxyCall             *call;
+} GdmUserManagerFetchUserRequest;
 
 struct GdmUserManagerPrivate
 {
-        GHashTable            *users;
+        GHashTable            *users_by_name;
+        GHashTable            *users_by_object_path;
         GHashTable            *sessions;
-        GHashTable            *exclusions;
         GHashTable            *shells;
         DBusGConnection       *connection;
-        DBusGProxy            *seat_proxy;
-        char                  *seat_id;
+        DBusGProxyCall        *get_sessions_call;
+        DBusGProxy            *accounts_proxy;
+
+        GdmUserManagerSeat     seat;
+
+        GSList                *new_sessions;
+        GSList                *new_users;
+        GSList                *fetch_user_requests;
 
         GFileMonitor          *passwd_monitor;
         GFileMonitor          *shells_monitor;
 
-        guint                  reload_id;
+        GSList                *exclude_usernames;
+        GSList                *include_usernames;
+        gboolean               include_all;
+
+        gboolean               load_passwd_pending;
+
+        guint                  load_id;
+        guint                  reload_passwd_id;
         guint                  ck_history_id;
+        guint                  ck_history_watchdog_id;
+        GPid                   ck_history_pid;
+
+        gboolean               is_loaded;
+        gboolean               has_multiple_users;
+        gboolean               listing_cached_users;
+};
 
-        guint8                 users_dirty : 1;
+enum {
+        PROP_0,
+        PROP_INCLUDE_ALL,
+        PROP_INCLUDE_USERNAMES_LIST,
+        PROP_EXCLUDE_USERNAMES_LIST,
+        PROP_IS_LOADED,
+        PROP_HAS_MULTIPLE_USERS
 };
 
 enum {
-        LOADING_USERS,
-        USERS_LOADED,
         USER_ADDED,
         USER_REMOVED,
         USER_IS_LOGGED_IN_CHANGED,
-        USER_LOGIN_FREQUENCY_CHANGED,
+        USER_CHANGED,
         LAST_SIGNAL
 };
 
@@ -133,6 +209,28 @@ 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 void     load_users_manually         (GdmUserManager *manager);
+static void     monitor_local_users         (GdmUserManager *manager);
+static void     load_seat_incrementally     (GdmUserManager *manager);
+static void     unload_seat                 (GdmUserManager *manager);
+static void     load_users                  (GdmUserManager *manager);
+static void     queue_load_seat_and_users   (GdmUserManager *manager);
+static void     monitor_local_users         (GdmUserManager *manager);
+
+static void     load_new_session_incrementally (GdmUserManagerNewSession *new_session);
+static void     set_is_loaded (GdmUserManager *manager, gboolean is_loaded);
+
+static void     on_new_user_loaded (GdmUser        *user,
+                                    GParamSpec     *pspec,
+                                    GdmUserManager *manager);
+static void     give_up_and_fetch_user_locally (GdmUserManager                 *manager,
+                                                GdmUserManagerFetchUserRequest *request);
+static void     fetch_user_locally             (GdmUserManager *manager,
+                                                GdmUser        *user,
+                                                const char     *username);
+static void     fetch_user_incrementally       (GdmUserManagerFetchUserRequest *request);
+
+static void     maybe_set_is_loaded            (GdmUserManager *manager);
 static gpointer user_manager_object = NULL;
 
 G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
@@ -156,76 +254,15 @@ start_new_login_session (GdmUserManager *manager)
 
         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;
+                if (error != NULL) {
+                        g_warning ("Unable to start new login: %s", error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Unable to start new login");
                 }
         }
 
- out:
-
-        return primary_ssid;
+        return res;
 }
 
 static gboolean
@@ -312,8 +349,12 @@ session_is_login_window (GdmUserManager *manager,
                                  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);
+                if (error != NULL) {
+                        g_debug ("GdmUserManager: Failed to identify the session type: %s", error->message);
+                        g_error_free (error);
+                } else {
+                        g_debug ("GdmUserManager: Failed to identify the session type");
+                }
                 goto out;
         }
 
@@ -341,46 +382,36 @@ _get_login_window_session_id (GdmUserManager *manager)
         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");
+        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;
-        }
+        can_activate_sessions = gdm_user_manager_can_switch (manager);
 
         if (! can_activate_sessions) {
-                g_debug ("GdmSlave: seat is unable to activate sessions");
+                g_debug ("GdmUserManager: seat is unable to activate sessions");
                 goto out;
         }
 
         error = NULL;
-        res = dbus_g_proxy_call (manager->priv->seat_proxy,
+        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);
+                if (error != NULL) {
+                        g_warning ("unable to determine sessions for user: %s",
+                                   error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("unable to determine sessions for user");
+                }
                 goto out;
         }
 
@@ -410,6 +441,7 @@ gdm_user_manager_goto_login_session (GdmUserManager *manager)
         char    *ssid;
 
         g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
+        g_return_val_if_fail (manager->priv->is_loaded, FALSE);
 
         ret = FALSE;
 
@@ -418,7 +450,7 @@ gdm_user_manager_goto_login_session (GdmUserManager *manager)
 
         ssid = _get_login_window_session_id (manager);
         if (ssid != NULL) {
-                res = activate_session_id (manager, manager->priv->seat_id, ssid);
+                res = activate_session_id (manager, manager->priv->seat.id, ssid);
                 if (res) {
                         ret = TRUE;
                 }
@@ -435,24 +467,73 @@ gdm_user_manager_goto_login_session (GdmUserManager *manager)
 }
 
 gboolean
+gdm_user_manager_can_switch (GdmUserManager *manager)
+{
+        gboolean    res;
+        gboolean    can_activate_sessions;
+        GError     *error;
+
+        if (!manager->priv->is_loaded) {
+                g_debug ("GdmUserManager: Unable to switch sessions until fully loaded");
+                return FALSE;
+        }
+
+        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 FALSE;
+        }
+
+        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) {
+                if (error != NULL) {
+                        g_warning ("unable to determine if seat can activate sessions: %s",
+                                   error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("unable to determine if seat can activate sessions");
+                }
+                return FALSE;
+        }
+
+        return can_activate_sessions;
+}
+
+gboolean
 gdm_user_manager_activate_user_session (GdmUserManager *manager,
                                         GdmUser        *user)
 {
         gboolean ret;
-        char    *ssid;
+        const char *ssid;
         gboolean res;
 
+        gboolean can_activate_sessions;
         g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
         g_return_val_if_fail (GDM_IS_USER (user), FALSE);
+        g_return_val_if_fail (manager->priv->is_loaded, FALSE);
 
         ret = FALSE;
 
-        ssid = _get_primary_user_session_id (manager, user);
+        can_activate_sessions = gdm_user_manager_can_switch (manager);
+
+        if (! can_activate_sessions) {
+                g_debug ("GdmUserManager: seat is unable to activate sessions");
+                goto out;
+        }
+
+        ssid = gdm_user_get_primary_session_id (user);
         if (ssid == NULL) {
                 goto out;
         }
 
-        res = activate_session_id (manager, manager->priv->seat_id, ssid);
+        res = activate_session_id (manager, manager->priv->seat.id, ssid);
         if (! res) {
                 g_debug ("GdmUserManager: unable to activate session: %s", ssid);
                 goto out;
@@ -469,6 +550,10 @@ on_user_sessions_changed (GdmUser        *user,
 {
         guint nsessions;
 
+        if (! manager->priv->is_loaded) {
+                return;
+        }
+
         nsessions = gdm_user_get_num_sessions (user);
 
         g_debug ("GdmUserManager: sessions changed user=%s num=%d",
@@ -484,385 +569,848 @@ on_user_sessions_changed (GdmUser        *user,
 }
 
 static void
-on_user_icon_changed (GdmUser        *user,
-                      GdmUserManager *manager)
+on_user_changed (GdmUser        *user,
+                 GdmUserManager *manager)
 {
-        g_debug ("GdmUserManager: user icon changed");
+        if (manager->priv->is_loaded) {
+                g_debug ("GdmUserManager: user changed");
+                g_signal_emit (manager, signals[USER_CHANGED], 0, user);
+        }
 }
 
-static char *
-get_seat_id_for_session (DBusGConnection *connection,
-                         const char      *session_id)
+static void
+on_get_seat_id_finished (DBusGProxy     *proxy,
+                         DBusGProxyCall *call,
+                         GdmUserManager *manager)
 {
-        DBusGProxy      *proxy;
-        GError          *error;
-        char            *seat_id;
-        gboolean         res;
+        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;
-        }
+        g_assert (manager->priv->seat.get_seat_id_call == call);
 
         error = NULL;
-        res = dbus_g_proxy_call (proxy,
-                                 "GetSeatId",
-                                 &error,
-                                 G_TYPE_INVALID,
-                                 DBUS_TYPE_G_OBJECT_PATH, &seat_id,
-                                 G_TYPE_INVALID);
+        seat_id = NULL;
+        res = dbus_g_proxy_end_call (proxy,
+                                     call,
+                                     &error,
+                                     DBUS_TYPE_G_OBJECT_PATH,
+                                     &seat_id,
+                                     G_TYPE_INVALID);
+        manager->priv->seat.get_seat_id_call = NULL;
+        g_object_unref (proxy);
+
         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);
+                if (error != NULL) {
+                        g_debug ("Failed to identify the seat of the "
+                                 "current session: %s",
+                                 error->message);
+                        g_error_free (error);
+                } else {
+                        g_debug ("Failed to identify the seat of the "
+                                 "current session");
+                }
+                unload_seat (manager);
+                maybe_set_is_loaded (manager);
+                return;
         }
 
-        return seat_id;
+        g_debug ("GdmUserManager: Found current seat: %s", seat_id);
+
+        manager->priv->seat.id = seat_id;
+        manager->priv->seat.state++;
+
+        load_seat_incrementally (manager);
 }
 
-static char *
-get_x11_display_for_session (DBusGConnection *connection,
-                             const char      *session_id)
+static void
+get_seat_id_for_current_session (GdmUserManager *manager)
 {
         DBusGProxy      *proxy;
-        GError          *error;
-        char            *x11_display;
-        gboolean         res;
+        DBusGProxyCall  *call;
 
-        proxy = NULL;
-        x11_display = NULL;
-
-        proxy = dbus_g_proxy_new_for_name (connection,
+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
                                            CK_NAME,
-                                           session_id,
+                                           manager->priv->seat.session_id,
                                            CK_SESSION_INTERFACE);
         if (proxy == NULL) {
                 g_warning ("Failed to connect to the ConsoleKit session object");
-                goto out;
+                goto failed;
         }
 
-        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);
+        call = dbus_g_proxy_begin_call (proxy,
+                                        "GetSeatId",
+                                        (DBusGProxyCallNotify)
+                                        on_get_seat_id_finished,
+                                        manager,
+                                        NULL,
+                                        G_TYPE_INVALID);
+        if (call == NULL) {
+                g_warning ("GdmUserManager: failed to make GetSeatId call");
+                goto failed;
         }
- out:
+
+        manager->priv->seat.get_seat_id_call = call;
+
+        return;
+
+failed:
         if (proxy != NULL) {
                 g_object_unref (proxy);
         }
 
-        return x11_display;
+        unload_seat (manager);
 }
 
-static gboolean
-maybe_add_session_for_user (GdmUserManager *manager,
-                            GdmUser        *user,
-                            const char     *ssid)
+static gint
+match_name_cmpfunc (gconstpointer a,
+                    gconstpointer b)
 {
-        char    *sid;
-        char    *x11_display;
-        gboolean ret;
+        return g_strcmp0 ((char *) a,
+                          (char *) b);
+}
 
-        ret = FALSE;
-        sid = NULL;
-        x11_display = NULL;
+static gboolean
+username_in_exclude_list (GdmUserManager *manager,
+                          const char     *username)
+{
+        GSList   *found;
+        gboolean  ret = FALSE;
 
-        /* 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;
+        /* always exclude the "gdm" user. */
+        if (username == NULL || (strcmp (username, GDM_USERNAME) == 0)) {
+                return TRUE;
         }
 
-        /* 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 (manager->priv->exclude_usernames != NULL) {
+                found = g_slist_find_custom (manager->priv->exclude_usernames,
+                                             username,
+                                             match_name_cmpfunc);
+                if (found != NULL) {
+                        ret = TRUE;
+                }
         }
 
-        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;
-        }
+        return ret;
+}
 
+static void
+add_session_for_user (GdmUserManager *manager,
+                      GdmUser        *user,
+                      const char     *ssid)
+{
         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;
+static void
+set_has_multiple_users (GdmUserManager *manager,
+                        gboolean        has_multiple_users)
+{
+        if (manager->priv->has_multiple_users != has_multiple_users) {
+                manager->priv->has_multiple_users = has_multiple_users;
+                g_object_notify (G_OBJECT (manager), "has-multiple-users");
+        }
+}
 
- out:
-        g_free (sid);
-        g_free (x11_display);
+static GdmUser *
+create_new_user (GdmUserManager *manager)
+{
+        GdmUser *user;
 
-        return ret;
+        user = g_object_new (GDM_TYPE_USER, NULL);
+
+        manager->priv->new_users = g_slist_prepend (manager->priv->new_users, user);
+
+        g_signal_connect (user, "notify::is-loaded", G_CALLBACK (on_new_user_loaded), manager);
+
+        return g_object_ref (user);
 }
 
 static void
-add_sessions_for_user (GdmUserManager *manager,
-                       GdmUser        *user)
+add_user (GdmUserManager *manager,
+          GdmUser        *user)
 {
-        DBusGProxy      *proxy;
-        GError          *error;
-        gboolean         res;
-        guint32          uid;
-        GPtrArray       *sessions;
-        int              i;
+        const char *object_path;
 
-        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;
+        g_hash_table_insert (manager->priv->users_by_name,
+                             g_strdup (gdm_user_get_user_name (user)),
+                             g_object_ref (user));
+
+        object_path = gdm_user_get_object_path (user);
+        if (object_path != NULL) {
+                g_hash_table_insert (manager->priv->users_by_object_path,
+                                     (gpointer) object_path,
+                                     g_object_ref (user));
         }
 
-        uid = gdm_user_get_uid (user);
+        g_signal_connect (user,
+                          "sessions-changed",
+                          G_CALLBACK (on_user_sessions_changed),
+                          manager);
+        g_signal_connect (user,
+                          "changed",
+                          G_CALLBACK (on_user_changed),
+                          manager);
 
-        g_debug ("Getting list of sessions for user %u", uid);
+        if (manager->priv->is_loaded) {
+                g_signal_emit (manager, signals[USER_ADDED], 0, user);
+        }
 
-        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;
+        if (g_hash_table_size (manager->priv->users_by_name) > 1) {
+                set_has_multiple_users (manager, TRUE);
         }
+}
 
-        g_debug ("Found %d sessions for user %s", sessions->len, gdm_user_get_user_name (user));
+static void
+remove_user (GdmUserManager *manager,
+             GdmUser        *user)
+{
+        g_object_ref (user);
 
-        for (i = 0; i < sessions->len; i++) {
-                char *ssid;
+        g_signal_handlers_disconnect_by_func (user, on_user_changed, manager);
+        g_signal_handlers_disconnect_by_func (user, on_user_sessions_changed, manager);
+        if (gdm_user_get_object_path (user) != NULL) {
+                g_hash_table_remove (manager->priv->users_by_object_path, gdm_user_get_object_path (user));
+        }
+        g_hash_table_remove (manager->priv->users_by_name, gdm_user_get_user_name (user));
 
-                ssid = g_ptr_array_index (sessions, i);
-                maybe_add_session_for_user (manager, user, ssid);
+        if (manager->priv->is_loaded) {
+                g_signal_emit (manager, signals[USER_REMOVED], 0, user);
         }
 
-        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
-        g_ptr_array_free (sessions, TRUE);
+        g_object_unref (user);
 
- out:
-        if (proxy != NULL) {
-                g_object_unref (proxy);
+        if (g_hash_table_size (manager->priv->users_by_name) > 1) {
+                set_has_multiple_users (manager, FALSE);
+        }
+}
+
+static void
+on_new_user_loaded (GdmUser        *user,
+                    GParamSpec     *pspec,
+                    GdmUserManager *manager)
+{
+        const char *username;
+        GdmUser *old_user;
+
+        if (!gdm_user_is_loaded (user)) {
+                return;
+        }
+
+        g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
+        manager->priv->new_users = g_slist_remove (manager->priv->new_users,
+                                                   user);
+
+        username = gdm_user_get_user_name (user);
+
+        if (username == NULL) {
+                const char *object_path;
+
+                object_path = gdm_user_get_object_path (user);
+
+                if (object_path != NULL) {
+                        g_warning ("GdmUserManager: user has no username "
+                                   "(object path: %s, uid: %lu)",
+                                   object_path, gdm_user_get_uid (user));
+                } else {
+                        g_warning ("GdmUserManager: user has no username (uid: %lu)",
+                                   gdm_user_get_uid (user));
+                }
+                g_object_unref (user);
+                return;
+        }
+
+        if (username_in_exclude_list (manager, username)) {
+                g_debug ("GdmUserManager: excluding user '%s'", username);
+                g_object_unref (user);
+                return;
+        }
+
+        old_user = g_hash_table_lookup (manager->priv->users_by_name, username);
+
+        /* If username got added earlier by a different means, trump it now.
+         */
+        if (old_user != NULL) {
+                remove_user (manager, old_user);
+        }
+
+        add_user (manager, user);
+        g_object_unref (user);
+
+        if (manager->priv->new_users == NULL) {
+                set_is_loaded (manager, TRUE);
         }
 }
 
 static GdmUser *
-create_user (GdmUserManager *manager)
+add_new_user_for_object_path (const char     *object_path,
+                              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);
+        user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path); 
+
+        if (user != NULL) {
+                return user;
+        }
+        user = create_new_user (manager);
+        _gdm_user_update_from_object_path (user, object_path);
+
         return user;
 }
 
 static void
-add_user (GdmUserManager *manager,
-          GdmUser        *user)
+on_new_user_in_accounts_service (DBusGProxy *proxy,
+                                 const char *object_path,
+                                 gpointer    user_data)
 {
-        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));
+        GdmUserManager *manager = GDM_USER_MANAGER (user_data);
 
-        g_signal_emit (manager, signals[USER_ADDED], 0, user);
+        add_new_user_for_object_path (object_path, manager);
 }
 
-static GdmUser *
-add_new_user_for_pwent (GdmUserManager *manager,
-                        struct passwd  *pwent)
+static void
+on_user_removed_in_accounts_service (DBusGProxy *proxy,
+                                     const char *object_path,
+                                     gpointer    user_data)
 {
-        GdmUser *user;
-
-        g_debug ("Creating new user");
+        GdmUserManager *manager = GDM_USER_MANAGER (user_data);
+        GdmUser        *user;
 
-        user = create_user (manager);
-        _gdm_user_update (user, pwent);
+        user = g_hash_table_lookup (manager->priv->users_by_object_path, object_path);
 
-        add_user (manager, user);
+        manager->priv->new_users = g_slist_remove (manager->priv->new_users, user);
 
-        return user;
+        remove_user (manager, user);
 }
 
-static char *
-get_current_seat_id (DBusGConnection *connection)
+static void
+on_get_current_session_finished (DBusGProxy     *proxy,
+                                 DBusGProxyCall *call,
+                                 GdmUserManager *manager)
 {
-        DBusGProxy      *proxy;
-        GError          *error;
-        char            *session_id;
-        char            *seat_id;
-        gboolean         res;
+        GError         *error;
+        char           *session_id;
+        gboolean        res;
+
+        g_assert (manager->priv->seat.get_current_session_call == call);
+        g_assert (manager->priv->seat.state == GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID);
 
-        proxy = NULL;
+        error = NULL;
         session_id = NULL;
-        seat_id = NULL;
+        res = dbus_g_proxy_end_call (proxy,
+                                     call,
+                                     &error,
+                                     DBUS_TYPE_G_OBJECT_PATH,
+                                     &session_id,
+                                     G_TYPE_INVALID);
+        manager->priv->seat.get_current_session_call = NULL;
+        g_object_unref (proxy);
 
-        proxy = dbus_g_proxy_new_for_name (connection,
+        if (! res) {
+                if (error != NULL) {
+                        g_debug ("Failed to identify the current session: %s",
+                                 error->message);
+                        g_error_free (error);
+                } else {
+                        g_debug ("Failed to identify the current session");
+                }
+                unload_seat (manager);
+                maybe_set_is_loaded (manager);
+                return;
+        }
+
+        manager->priv->seat.session_id = session_id;
+        manager->priv->seat.state++;
+
+        load_seat_incrementally (manager);
+}
+
+static void
+get_current_session_id (GdmUserManager *manager)
+{
+        DBusGProxy      *proxy;
+        DBusGProxyCall  *call;
+
+        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;
+                goto failed;
         }
 
-        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;
+        call = dbus_g_proxy_begin_call (proxy,
+                                        "GetCurrentSession",
+                                        (DBusGProxyCallNotify)
+                                        on_get_current_session_finished,
+                                        manager,
+                                        NULL,
+                                        G_TYPE_INVALID);
+        if (call == NULL) {
+                g_warning ("GdmUserManager: failed to make GetCurrentSession call");
+                goto failed;
         }
 
-        seat_id = get_seat_id_for_session (connection, session_id);
+        manager->priv->seat.get_current_session_call = call;
 
- out:
+        return;
+
+failed:
         if (proxy != NULL) {
                 g_object_unref (proxy);
         }
-        g_free (session_id);
 
-        return seat_id;
+        unload_seat (manager);
 }
 
-static gboolean
-get_uid_from_session_id (GdmUserManager *manager,
-                         const char     *session_id,
-                         uid_t          *uidp)
+static void
+unload_new_session (GdmUserManagerNewSession *new_session)
 {
-        DBusGProxy      *proxy;
-        GError          *error;
-        guint            uid;
-        gboolean         res;
+        GdmUserManager *manager;
+
+        manager = new_session->manager;
+
+        manager->priv->new_sessions = g_slist_remove (manager->priv->new_sessions,
+                                                      new_session);
+
+        if (new_session->proxy != NULL) {
+                g_object_unref (new_session->proxy);
+        }
+
+        g_free (new_session->x11_display);
+        g_free (new_session->id);
+
+        g_slice_free (GdmUserManagerNewSession, new_session);
+}
+
+static void
+get_proxy_for_new_session (GdmUserManagerNewSession *new_session)
+{
+        GdmUserManager *manager;
+        DBusGProxy     *proxy;
+
+        manager = new_session->manager;
 
         proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
                                            CK_NAME,
-                                           session_id,
+                                           new_session->id,
                                            CK_SESSION_INTERFACE);
         if (proxy == NULL) {
-                g_warning ("Failed to connect to the ConsoleKit session object");
-                return FALSE;
+                g_warning ("Failed to connect to the ConsoleKit '%s' object",
+                           new_session->id);
+                unload_new_session (new_session);
+                return;
         }
 
+        new_session->proxy = proxy;
+        new_session->state++;
+
+        load_new_session_incrementally (new_session);
+}
+
+static void
+on_get_unix_user_finished (DBusGProxy               *proxy,
+                           DBusGProxyCall           *call,
+                           GdmUserManagerNewSession *new_session)
+{
+        GdmUserManager *manager;
+        GError         *error;
+        guint           uid;
+        gboolean        res;
+
+        manager = new_session->manager;
+
+        g_assert (new_session->get_unix_user_call == call);
+
         error = NULL;
-        res = dbus_g_proxy_call (proxy,
-                                 "GetUnixUser",
-                                 &error,
-                                 G_TYPE_INVALID,
-                                 G_TYPE_UINT, &uid,
-                                 G_TYPE_INVALID);
-        g_object_unref (proxy);
+
+        uid = (guint) -1;
+        res = dbus_g_proxy_end_call (proxy,
+                                     call,
+                                     &error,
+                                     G_TYPE_UINT, &uid,
+                                     G_TYPE_INVALID);
+        new_session->get_unix_user_call = NULL;
 
         if (! res) {
-                g_warning ("Failed to query the session: %s", error->message);
-                g_error_free (error);
-                return FALSE;
+                if (error != NULL) {
+                        g_debug ("Failed to get uid of session '%s': %s",
+                                 new_session->id, error->message);
+                        g_error_free (error);
+                } else {
+                        g_debug ("Failed to get uid of session '%s'",
+                                 new_session->id);
+                }
+                unload_new_session (new_session);
+                return;
         }
 
-        if (uidp != NULL) {
-                *uidp = (uid_t) uid;
+        g_debug ("GdmUserManager: Found uid of session '%s': %u",
+                 new_session->id, uid);
+
+        new_session->uid = (uid_t) uid;
+        new_session->state++;
+
+        load_new_session_incrementally (new_session);
+}
+
+static void
+get_uid_for_new_session (GdmUserManagerNewSession *new_session)
+{
+        DBusGProxyCall *call;
+
+        g_assert (new_session->proxy != NULL);
+
+        call = dbus_g_proxy_begin_call (new_session->proxy,
+                                        "GetUnixUser",
+                                        (DBusGProxyCallNotify)
+                                        on_get_unix_user_finished,
+                                        new_session,
+                                        NULL,
+                                        G_TYPE_INVALID);
+        if (call == NULL) {
+                g_warning ("GdmUserManager: failed to make GetUnixUser call");
+                goto failed;
         }
 
-        return TRUE;
+        new_session->get_unix_user_call = call;
+        return;
+
+failed:
+        unload_new_session (new_session);
 }
 
 static void
-seat_session_added (DBusGProxy     *seat_proxy,
-                    const char     *session_id,
-                    GdmUserManager *manager)
+on_find_user_by_name_finished (DBusGProxy                     *proxy,
+                               DBusGProxyCall                 *call,
+                               GdmUserManagerFetchUserRequest *request)
 {
-        uid_t          uid;
-        gboolean       res;
-        struct passwd *pwent;
-        GdmUser       *user;
-        gboolean       is_new;
+        GdmUserManager  *manager;
+        GError          *error;
+        char            *object_path;
+        gboolean         res;
 
-        g_debug ("Session added: %s", session_id);
+        g_assert (request->call == call);
 
-        res = get_uid_from_session_id (manager, session_id, &uid);
+        error = NULL;
+        object_path = NULL;
+        manager = request->manager;
+        res = dbus_g_proxy_end_call (manager->priv->accounts_proxy,
+                                     call,
+                                     &error,
+                                     DBUS_TYPE_G_OBJECT_PATH,
+                                     &object_path,
+                                     G_TYPE_INVALID);
         if (! res) {
-                g_warning ("Unable to lookup user for session");
+                if (error != NULL) {
+                        g_debug ("GdmUserManager: Failed to find user %s: %s",
+                                 request->username, error->message);
+                        g_error_free (error);
+                } else {
+                        g_debug ("GdmUserManager: Failed to find user %s",
+                                 request->username);
+                }
+                give_up_and_fetch_user_locally (manager, request);
                 return;
         }
 
+        g_debug ("GdmUserManager: Found object path of user '%s': %s",
+                 request->username, object_path);
+        request->object_path = object_path;
+        request->state++;
+
+        fetch_user_incrementally (request);
+}
+
+static void
+find_user_in_accounts_service (GdmUserManager                 *manager,
+                               GdmUserManagerFetchUserRequest *request)
+{
+        DBusGProxyCall  *call;
+
+        g_debug ("GdmUserManager: Looking for user %s in accounts service",
+                 request->username);
+
+        g_assert (manager->priv->accounts_proxy != NULL);
+
+        call = dbus_g_proxy_begin_call (manager->priv->accounts_proxy,
+                                        "FindUserByName",
+                                        (DBusGProxyCallNotify)
+                                        on_find_user_by_name_finished,
+                                        request,
+                                        NULL,
+                                        G_TYPE_STRING,
+                                        request->username,
+                                        G_TYPE_INVALID);
+
+        if (call == NULL) {
+                g_warning ("GdmUserManager: failed to make FindUserByName('%s') call",
+                           request->username);
+                goto failed;
+        }
+
+        request->call = call;
+        return;
+
+failed:
+        give_up_and_fetch_user_locally (manager, request);
+}
+
+static void
+set_is_loaded (GdmUserManager *manager,
+               gboolean        is_loaded)
+{
+        if (manager->priv->is_loaded != is_loaded) {
+                manager->priv->is_loaded = is_loaded;
+                g_object_notify (G_OBJECT (manager), "is-loaded");
+        }
+}
+
+static void
+on_list_cached_users_finished (DBusGProxy     *proxy,
+                               DBusGProxyCall *call_id,
+                               gpointer        data)
+{
+        GdmUserManager *manager = data;
+        GError *error = NULL;
+        GPtrArray *paths;
+
+        manager->priv->listing_cached_users = FALSE;
+        if (!dbus_g_proxy_end_call (proxy,
+                                    call_id,
+                                    &error,
+                                    dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &paths,
+                                    G_TYPE_INVALID)) {
+                g_debug ("GdmUserManager: ListCachedUsers failed: %s", error->message);
+                g_error_free (error);
+
+                g_object_unref (manager->priv->accounts_proxy);
+                manager->priv->accounts_proxy = NULL;
+
+                load_users_manually (manager);
+
+                return;
+        }
+
+        g_ptr_array_foreach (paths, (GFunc)add_new_user_for_object_path, manager);
+
+        g_ptr_array_foreach (paths, (GFunc)g_free, NULL);
+        g_ptr_array_free (paths, TRUE);
+
+        /* Add users who are specifically included */
+        if (manager->priv->include_usernames != NULL) {
+                GSList *l;
+
+                for (l = manager->priv->include_usernames; l != NULL; l = l->next) {
+                        GdmUser *user;
+
+                        g_debug ("GdmUserManager: Adding included user %s", (char *)l->data);
+                        /*
+                         * The call to gdm_user_manager_get_user will add the user if it is
+                         * valid and not already in the hash.
+                         */
+                        user = gdm_user_manager_get_user (manager, l->data);
+                        if (user == NULL) {
+                                g_debug ("GdmUserManager: unable to lookup user '%s'", (char *)l->data);
+                        }
+                }
+        }
+}
+
+static void
+on_get_x11_display_finished (DBusGProxy               *proxy,
+                             DBusGProxyCall           *call,
+                             GdmUserManagerNewSession *new_session)
+{
+        GError   *error;
+        char     *x11_display;
+        gboolean  res;
+
+        g_assert (new_session->get_x11_display_call == call);
+
+        error = NULL;
+        x11_display = NULL;
+        res = dbus_g_proxy_end_call (proxy,
+                                     call,
+                                     &error,
+                                     G_TYPE_STRING,
+                                     &x11_display,
+                                     G_TYPE_INVALID);
+        new_session->get_x11_display_call = NULL;
+
+        if (! res) {
+                if (error != NULL) {
+                        g_debug ("Failed to get the x11 display of session '%s': %s",
+                                 new_session->id, error->message);
+                        g_error_free (error);
+                } else {
+                        g_debug ("Failed to get the x11 display of session '%s'",
+                                 new_session->id);
+                }
+                unload_new_session (new_session);
+                return;
+        }
+
+        g_debug ("GdmUserManager: Found x11 display of session '%s': %s",
+                 new_session->id, x11_display);
+
+        new_session->x11_display = x11_display;
+        new_session->state++;
+
+        load_new_session_incrementally (new_session);
+}
+
+static void
+get_x11_display_for_new_session (GdmUserManagerNewSession *new_session)
+{
+        DBusGProxyCall *call;
+
+        g_assert (new_session->proxy != NULL);
+
+        call = dbus_g_proxy_begin_call (new_session->proxy,
+                                        "GetX11Display",
+                                        (DBusGProxyCallNotify)
+                                        on_get_x11_display_finished,
+                                        new_session,
+                                        NULL,
+                                        G_TYPE_INVALID);
+        if (call == NULL) {
+                g_warning ("GdmUserManager: failed to make GetX11Display call");
+                goto failed;
+        }
+
+        new_session->get_x11_display_call = call;
+        return;
+
+failed:
+        unload_new_session (new_session);
+}
+
+static gboolean
+get_pwent_for_name (const char     *name,
+                    struct passwd **pwentp)
+{
+        struct passwd *pwent;
+
+        do {
+                errno = 0;
+                pwent = getpwnam (name);
+        } while (pwent == NULL && errno == EINTR);
+
+        if (pwentp != NULL) {
+                *pwentp = pwent;
+        }
+
+        return (pwent != NULL);
+}
+
+static gboolean
+get_pwent_for_uid (uid_t           uid,
+                   struct passwd **pwentp)
+{
+        struct passwd *pwent;
+
+        do {
+                errno = 0;
+                pwent = getpwuid (uid);
+        } while (pwent == NULL && errno == EINTR);
+
+        if (pwentp != NULL) {
+                *pwentp = pwent;
+        }
+
+        return (pwent != NULL);
+}
+
+static void
+maybe_add_new_session (GdmUserManagerNewSession *new_session)
+{
+        GdmUserManager *manager;
+        struct passwd  *pwent;
+        GdmUser        *user;
+
+        manager = GDM_USER_MANAGER (new_session->manager);
+
         errno = 0;
-        pwent = getpwuid (uid);
+        get_pwent_for_uid (new_session->uid, &pwent);
         if (pwent == NULL) {
-                g_warning ("Unable to lookup user id %d: %s", (int)uid, g_strerror (errno));
-                return;
+                g_warning ("Unable to lookup user ID %d: %s",
+                           (int) new_session->uid, g_strerror (errno));
+                goto failed;
         }
 
         /* check exclusions up front */
-        if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
+        if (username_in_exclude_list (manager, pwent->pw_name)) {
                 g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
-                return;
+                goto failed;
         }
 
-        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+        user = gdm_user_manager_get_user (manager, 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;
+                return;
         }
 
-        res = maybe_add_session_for_user (manager, user, session_id);
+        add_session_for_user (manager, user, new_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);
-                }
+        /* if we haven't yet gotten the login frequency
+           then at least add one because the session exists */
+        if (gdm_user_get_login_frequency (user) == 0) {
+                _gdm_user_update_login_frequency (user, 1);
         }
+
+        manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_LOADED;
+        unload_new_session (new_session);
+        return;
+
+failed:
+        unload_new_session (new_session);
+}
+
+static void
+load_new_session (GdmUserManager *manager,
+                  const char     *session_id)
+{
+        GdmUserManagerNewSession *new_session;
+
+        new_session = g_slice_new0 (GdmUserManagerNewSession);
+
+        new_session->manager = manager;
+        new_session->id = g_strdup (session_id);
+        new_session->state = GDM_USER_MANAGER_NEW_SESSION_STATE_UNLOADED + 1;
+
+        manager->priv->new_sessions = g_slist_prepend (manager->priv->new_sessions,
+                                                       new_session);
+        load_new_session_incrementally (new_session);
+}
+
+static void
+seat_session_added (DBusGProxy     *seat_proxy,
+                    const char     *session_id,
+                    GdmUserManager *manager)
+{
+        g_debug ("GdmUserManager: Session added: %s", session_id);
+
+        load_new_session (manager, session_id);
+}
+
+static gint
+match_new_session_cmpfunc (gconstpointer a,
+                           gconstpointer b)
+{
+        GdmUserManagerNewSession *new_session;
+        const char               *session_id;
+
+        new_session = (GdmUserManagerNewSession *) a;
+        session_id = (const char *) b;
+
+        return strcmp (new_session->id, session_id);
 }
 
 static void
@@ -870,10 +1418,36 @@ seat_session_removed (DBusGProxy     *seat_proxy,
                       const char     *session_id,
                       GdmUserManager *manager)
 {
-        GdmUser *user;
-        char    *username;
+        GdmUser       *user;
+        GSList        *found;
+        char          *username;
 
-        g_debug ("Session removed: %s", session_id);
+        g_debug ("GdmUserManager: Session removed: %s", session_id);
+
+        found = g_slist_find_custom (manager->priv->new_sessions,
+                                     session_id,
+                                     match_new_session_cmpfunc);
+
+        if (found != NULL) {
+                GdmUserManagerNewSession *new_session;
+
+                new_session = (GdmUserManagerNewSession *) found->data;
+
+                if (new_session->state > GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY) {
+                        g_debug ("GdmUserManager: New session for uid %d on "
+                                 "x11 display %s removed before fully loading",
+                                 (int) new_session->uid, new_session->x11_display);
+                } else if (new_session->state > GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID) {
+                        g_debug ("GdmUserManager: New session for uid %d "
+                                 "removed before fully loading",
+                                 (int) new_session->uid);
+                } else {
+                        g_debug ("GdmUserManager: New session removed "
+                                 "before fully loading");
+                }
+                unload_new_session (new_session);
+                return;
+        }
 
         /* since the session object may already be gone
          * we can't query CK directly */
@@ -883,7 +1457,7 @@ seat_session_removed (DBusGProxy     *seat_proxy,
                 return;
         }
 
-        user = g_hash_table_lookup (manager->priv->users, username);
+        user = g_hash_table_lookup (manager->priv->users_by_name, username);
         if (user == NULL) {
                 /* nothing to do */
                 return;
@@ -894,12 +1468,12 @@ seat_session_removed (DBusGProxy     *seat_proxy,
 }
 
 static void
-on_proxy_destroy (DBusGProxy     *proxy,
-                  GdmUserManager *manager)
+on_seat_proxy_destroy (DBusGProxy     *proxy,
+                       GdmUserManager *manager)
 {
         g_debug ("GdmUserManager: seat proxy destroyed");
 
-        manager->priv->seat_proxy = NULL;
+        manager->priv->seat.proxy = NULL;
 }
 
 static void
@@ -908,37 +1482,28 @@ 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);
+        g_assert (manager->priv->seat.proxy == NULL);
 
         error = NULL;
         proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
                                                  CK_NAME,
-                                                 manager->priv->seat_id,
+                                                 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);
+                if (error != NULL) {
+                        g_warning ("Failed to connect to the ConsoleKit seat object: %s",
+                                   error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Failed to connect to the ConsoleKit seat object");
+                }
+                unload_seat (manager);
                 return;
         }
 
-        g_signal_connect (proxy, "destroy", G_CALLBACK (on_proxy_destroy), manager);
+        g_signal_connect (proxy, "destroy", G_CALLBACK (on_seat_proxy_destroy), manager);
 
         dbus_g_proxy_add_signal (proxy,
                                  "SessionAdded",
@@ -958,8 +1523,205 @@ get_seat_proxy (GdmUserManager *manager)
                                      G_CALLBACK (seat_session_removed),
                                      manager,
                                      NULL);
-        manager->priv->seat_proxy = proxy;
+        manager->priv->seat.proxy = proxy;
+        manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_LOADED;
+}
+
+static void
+unload_seat (GdmUserManager *manager)
+{
+        manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED;
+
+        if (manager->priv->seat.proxy != NULL) {
+                g_object_unref (manager->priv->seat.proxy);
+                manager->priv->seat.proxy = NULL;
+        }
+
+        g_free (manager->priv->seat.id);
+        manager->priv->seat.id = NULL;
+
+        g_free (manager->priv->seat.session_id);
+        manager->priv->seat.session_id = NULL;
+}
+
+static void
+get_accounts_proxy (GdmUserManager *manager)
+{
+        DBusGProxy      *proxy;
+        GError          *error;
+
+        g_assert (manager->priv->accounts_proxy == NULL);
+
+        error = NULL;
+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
+                                           ACCOUNTS_NAME,
+                                           ACCOUNTS_PATH,
+                                           ACCOUNTS_INTERFACE);
+        manager->priv->accounts_proxy = proxy;
+
+        dbus_g_proxy_add_signal (proxy,
+                                 "UserAdded",
+                                 DBUS_TYPE_G_OBJECT_PATH,
+                                 G_TYPE_INVALID);
+        dbus_g_proxy_add_signal (proxy,
+                                 "UserDeleted",
+                                 DBUS_TYPE_G_OBJECT_PATH,
+                                 G_TYPE_INVALID);
+
+        dbus_g_proxy_connect_signal (proxy,
+                                     "UserAdded",
+                                     G_CALLBACK (on_new_user_in_accounts_service),
+                                     manager,
+                                     NULL);
+        dbus_g_proxy_connect_signal (proxy,
+                                     "UserDeleted",
+                                     G_CALLBACK (on_user_removed_in_accounts_service),
+                                     manager,
+                                     NULL);
+}
+
+static void
+load_new_session_incrementally (GdmUserManagerNewSession *new_session)
+{
+        switch (new_session->state) {
+        case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_PROXY:
+                get_proxy_for_new_session (new_session);
+                break;
+        case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_UID:
+                get_uid_for_new_session (new_session);
+                break;
+        case GDM_USER_MANAGER_NEW_SESSION_STATE_GET_X11_DISPLAY:
+                get_x11_display_for_new_session (new_session);
+                break;
+        case GDM_USER_MANAGER_NEW_SESSION_STATE_MAYBE_ADD:
+                maybe_add_new_session (new_session);
+                break;
+        case GDM_USER_MANAGER_NEW_SESSION_STATE_LOADED:
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+}
+
+static void
+free_fetch_user_request (GdmUserManagerFetchUserRequest *request)
+{
+        GdmUserManager *manager;
+
+        manager = request->manager;
+
+        manager->priv->fetch_user_requests = g_slist_remove (manager->priv->fetch_user_requests, request);
+        g_free (request->username);
+        g_free (request->object_path);
+        g_slice_free (GdmUserManagerFetchUserRequest, request);
+}
+
+static void
+give_up_and_fetch_user_locally (GdmUserManager                 *manager,
+                                GdmUserManagerFetchUserRequest *request)
+{
+
+        g_debug ("GdmUserManager: account service unavailable, "
+                 "fetching user %s locally",
+                 request->username);
+        fetch_user_locally (manager, request->user, request->username);
+        request->state = GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED;
+}
+
+static void
+on_user_manager_maybe_ready_for_request (GdmUserManager                 *manager,
+                                         GParamSpec                     *pspec,
+                                         GdmUserManagerFetchUserRequest *request)
+{
+        if (!manager->priv->is_loaded) {
+                return;
+        }
+
+        g_signal_handlers_disconnect_by_func (manager, on_user_manager_maybe_ready_for_request, request);
+
+        request->state++;
+        fetch_user_incrementally (request);
+}
+
+static void
+fetch_user_incrementally (GdmUserManagerFetchUserRequest *request)
+{
+        GdmUserManager *manager;
+
+        g_debug ("GdmUserManager: finding user %s state %d",
+                 request->username, request->state);
+        manager = request->manager;
+        switch (request->state) {
+        case GDM_USER_MANAGER_GET_USER_STATE_WAIT_FOR_LOADED:
+                if (manager->priv->is_loaded) {
+                        request->state++;
+                        fetch_user_incrementally (request);
+                } else {
+                        g_debug ("GdmUserManager: waiting for user manager to load before finding user %s",
+                                 request->username);
+                        g_signal_connect (manager, "notify::is-loaded",
+                                          G_CALLBACK (on_user_manager_maybe_ready_for_request), request);
+
+                }
+                break;
+
+        case GDM_USER_MANAGER_GET_USER_STATE_ASK_ACCOUNTS_SERVICE:
+                if (manager->priv->accounts_proxy == NULL) {
+                        give_up_and_fetch_user_locally (manager, request);
+                } else {
+                        find_user_in_accounts_service (manager, request);
+                }
+                break;
+        case GDM_USER_MANAGER_GET_USER_STATE_FETCHED:
+                g_debug ("GdmUserManager: user %s fetched", request->username);
+                _gdm_user_update_from_object_path (request->user, request->object_path);
+                break;
+        case GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED:
+                g_debug ("GdmUserManager: user %s was not fetched", request->username);
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+
+        if (request->state == GDM_USER_MANAGER_GET_USER_STATE_FETCHED  ||
+            request->state == GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED) {
+                g_debug ("GdmUserManager: finished handling request for user %s",
+                         request->username);
+                free_fetch_user_request (request);
+        }
+}
+
+static void
+fetch_user_from_accounts_service (GdmUserManager *manager,
+                                  GdmUser        *user,
+                                  const char     *username)
+{
+        GdmUserManagerFetchUserRequest *request;
+
+        request = g_slice_new0 (GdmUserManagerFetchUserRequest);
 
+        request->manager = manager;
+        request->username = g_strdup (username);
+        request->user = user;
+        request->state = GDM_USER_MANAGER_GET_USER_STATE_UNFETCHED + 1;
+
+        manager->priv->fetch_user_requests = g_slist_prepend (manager->priv->fetch_user_requests,
+                                                              request);
+        fetch_user_incrementally (request);
+}
+
+static void
+fetch_user_locally (GdmUserManager *manager,
+                    GdmUser        *user,
+                    const char     *username)
+{
+        struct passwd *pwent;
+
+        get_pwent_for_name (username, &pwent);
+
+        if (pwent != NULL) {
+                _gdm_user_update_from_pwent (user, pwent);
+        }
 }
 
 /**
@@ -967,10 +1729,12 @@ get_seat_proxy (GdmUserManager *manager)
  * @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.
+ * Retrieves a pointer to the #GdmUser object for the login @username
+ * from @manager. Trying to use this object before its
+ * #GdmUser:is-loaded property is %TRUE will result in undefined
+ * behavior.
  *
- * Returns: (transfer none): a pointer to a #GdmUser object.
+ * Returns: (transfer none): #GdmUser object
  **/
 GdmUser *
 gdm_user_manager_get_user (GdmUserManager *manager,
@@ -981,54 +1745,49 @@ gdm_user_manager_get_user (GdmUserManager *manager,
         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);
+        user = g_hash_table_lookup (manager->priv->users_by_name, username);
 
+        /* if we don't have it loaded try to load it now */
         if (user == NULL) {
-                struct passwd *pwent;
-
-                pwent = getpwnam (username);
+                user = create_new_user (manager);
 
-                if (pwent != NULL) {
-                        user = add_new_user_for_pwent (manager, pwent);
+                if (manager->priv->accounts_proxy != NULL) {
+                        fetch_user_from_accounts_service (manager, user, username);
+                } else {
+                        fetch_user_locally (manager, user, username);
                 }
         }
 
         return user;
 }
 
-
 /**
  * gdm_user_manager_get_user_by_uid:
  * @manager: the manager to query.
- * @uid: the user id
+ * @uid: the uid of the user to get.
  *
- * Retrieves a pointer to the #GdmUser object for the login named @username
- * from @manager.
+ * Retrieves a pointer to the #GdmUser object for the uid @uid
+ * from @manager. Trying to use this object before its
+ * #GdmUser:is-loaded property is %TRUE will result in undefined
+ * behavior.
  *
- * Returns: (transfer none): a pointer to a #GdmUser object.
- **/
+ * Returns: (transfer none): #GdmUser object
+ */
 GdmUser *
 gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
                                   gulong          uid)
 {
-        GdmUser       *user;
         struct passwd *pwent;
 
         g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
 
-        pwent = getpwuid (uid);
+        get_pwent_for_uid (uid, &pwent);
         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;
+        return gdm_user_manager_get_user (manager, pwent->pw_name);
 }
 
 static void
@@ -1043,8 +1802,11 @@ listify_hash_values_hfunc (gpointer key,
 
 /**
  * gdm_user_manager_list_users:
+ * @manager: a #GdmUserManager
  *
- * Return value: (element-type GdmUser) (transfer container): list of #GdmUser elements
+ * Get a list of system user accounts
+ *
+ * Returns: (element-type GdmUser) (transfer full): List of #GdmUser objects
  */
 GSList *
 gdm_user_manager_list_users (GdmUserManager *manager)
@@ -1054,7 +1816,7 @@ gdm_user_manager_list_users (GdmUserManager *manager)
         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);
+        g_hash_table_foreach (manager->priv->users_by_name, listify_hash_values_hfunc, &retval);
 
         return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
 }
@@ -1102,7 +1864,11 @@ parse_ck_history_line (const char *line,
         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);
+                if (error != NULL) {
+                        g_critical ("%s", error->message);
+                } else {
+                        g_critical ("Error in regex call");
+                }
                 goto out;
         }
 
@@ -1156,7 +1922,7 @@ process_ck_history_line (GdmUserManager *manager,
                 return;
         }
 
-        if (g_hash_table_lookup (manager->priv->exclusions, username)) {
+        if (username_in_exclude_list (manager, username)) {
                 g_debug ("GdmUserManager: excluding user '%s'", username);
                 g_free (username);
                 return;
@@ -1169,11 +1935,44 @@ process_ck_history_line (GdmUserManager *manager,
                 return;
         }
 
-        g_object_set (user, "login-frequency", frequency, NULL);
-        g_signal_emit (manager, signals [USER_LOGIN_FREQUENCY_CHANGED], 0, user);
+        _gdm_user_update_login_frequency (user, frequency);
         g_free (username);
 }
 
+static void
+maybe_set_is_loaded (GdmUserManager *manager)
+{
+        if (manager->priv->is_loaded) {
+                return;
+        }
+
+        if (manager->priv->ck_history_pid != 0) {
+                return;
+        }
+
+        if (manager->priv->load_passwd_pending) {
+                return;
+        }
+
+        if (manager->priv->get_sessions_call != NULL) {
+                return;
+        }
+
+        if (manager->priv->listing_cached_users) {
+                return;
+        }
+
+        /* Don't set is_loaded yet unless the seat is already loaded
+         * or failed to load.
+         */
+        if (manager->priv->seat.state != GDM_USER_MANAGER_SEAT_STATE_LOADED
+            && manager->priv->seat.state != GDM_USER_MANAGER_SEAT_STATE_UNLOADED) {
+                return;
+        }
+
+        set_is_loaded (manager, TRUE);
+}
+
 static gboolean
 ck_history_watch (GIOChannel     *source,
                   GIOCondition    condition,
@@ -1208,45 +2007,105 @@ ck_history_watch (GIOChannel     *source,
         }
 
         if (done) {
-                g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
-
                 manager->priv->ck_history_id = 0;
+                if (manager->priv->ck_history_watchdog_id != 0) {
+                        g_source_remove (manager->priv->ck_history_watchdog_id);
+                        manager->priv->ck_history_watchdog_id = 0;
+                }
+                manager->priv->ck_history_pid = 0;
+
+                maybe_set_is_loaded (manager);
+
                 return FALSE;
         }
 
         return TRUE;
 }
 
-static void
-reload_ck_history (GdmUserManager *manager)
+static int
+signal_pid (int pid,
+            int signal)
+{
+        int status = -1;
+
+        status = kill (pid, signal);
+
+        if (status < 0) {
+                if (errno == ESRCH) {
+                        g_debug ("Child process %lu was already dead.",
+                                 (unsigned long) pid);
+                } else {
+                        char buf [1024];
+                        snprintf (buf,
+                                  sizeof (buf),
+                                  "Couldn't kill child process %lu",
+                                  (unsigned long) pid);
+                        perror (buf);
+                }
+        }
+
+        return status;
+}
+
+static gboolean
+ck_history_watchdog (GdmUserManager *manager)
+{
+        if (manager->priv->ck_history_pid > 0) {
+                g_debug ("Killing ck-history process");
+                signal_pid (manager->priv->ck_history_pid, SIGTERM);
+                manager->priv->ck_history_pid = 0;
+        }
+
+        manager->priv->ck_history_watchdog_id = 0;
+        return FALSE;
+}
+
+static gboolean
+load_ck_history (GdmUserManager *manager)
 {
         char       *command;
+        char       *since;
         const char *seat_id;
         GError     *error;
         gboolean    res;
         char      **argv;
         int         standard_out;
         GIOChannel *channel;
+        GTimeVal    tv;
+
+        g_assert (manager->priv->ck_history_id == 0);
+
+        command = NULL;
 
         seat_id = NULL;
-        if (manager->priv->seat_id != NULL
-            && g_str_has_prefix (manager->priv->seat_id, "/org/freedesktop/ConsoleKit/")) {
+        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/");
+                seat_id = manager->priv->seat.id + strlen ("/org/freedesktop/ConsoleKit/");
         }
 
         if (seat_id == NULL) {
-                g_debug ("Unable to find users: no seat-id found");
-                return;
+                g_warning ("Unable to load CK history: no seat-id found");
+                goto out;
         }
 
-        command = g_strdup_printf ("ck-history --frequent --seat='%s' --session-type=''",
+        g_get_current_time (&tv);
+        tv.tv_sec -= LOGIN_FREQUENCY_TIME_WINDOW_SECS;
+        since = g_time_val_to_iso8601 (&tv);
+
+        command = g_strdup_printf ("ck-history --frequent --since='%s' --seat='%s' --session-type=''",
+                                   since,
                                    seat_id);
+        g_free (since);
         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);
+                if (error != NULL) {
+                        g_warning ("Could not parse command: %s", error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Could not parse command");
+                }
                 goto out;
         }
 
@@ -1257,15 +2116,19 @@ reload_ck_history (GdmUserManager *manager)
                                         G_SPAWN_SEARCH_PATH,
                                         NULL,
                                         NULL,
-                                        NULL, /* pid */
+                                        &manager->priv->ck_history_pid, /* 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);
+                if (error != NULL) {
+                        g_warning ("Unable to run ck-history: %s", error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Unable to run ck-history");
+                }
                 goto out;
         }
 
@@ -1274,6 +2137,7 @@ reload_ck_history (GdmUserManager *manager)
         g_io_channel_set_flags (channel,
                                 g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
                                 NULL);
+        manager->priv->ck_history_watchdog_id = g_timeout_add_seconds (1, (GSourceFunc) ck_history_watchdog, manager);
         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,
@@ -1281,20 +2145,31 @@ reload_ck_history (GdmUserManager *manager)
         g_io_channel_unref (channel);
 
  out:
+
         g_free (command);
+
+        return manager->priv->ck_history_id != 0;
 }
 
 static void
-reload_passwd (GdmUserManager *manager)
+reload_passwd_file (GHashTable *valid_shells,
+                    GSList     *exclude_users,
+                    GSList     *include_users,
+                    gboolean    include_all,
+                    GHashTable *current_users_by_name,
+                    GSList    **added_users,
+                    GSList    **removed_users)
 {
-        struct passwd *pwent;
-        GSList        *old_users;
-        GSList        *new_users;
-        GSList        *list;
-        FILE          *fp;
+        FILE           *fp;
+        GHashTableIter  iter;
+        GHashTable     *new_users_by_name;
+        GdmUser        *user;
+        char           *name;
 
-        old_users = NULL;
-        new_users = NULL;
+        new_users_by_name = g_hash_table_new_full (g_str_hash,
+                                                   g_str_equal,
+                                                   NULL,
+                                                   g_object_unref);
 
         errno = 0;
         fp = fopen (PATH_PASSWD, "r");
@@ -1303,74 +2178,127 @@ reload_passwd (GdmUserManager *manager)
                 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;
-                }
+        g_hash_table_iter_init (&iter, current_users_by_name);
+        while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+                struct passwd *pwent;
 
-                /* ...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);
+                get_pwent_for_name (name, &pwent);
+                if (pwent == NULL) {
                         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;
+                g_object_freeze_notify (G_OBJECT (user));
+                _gdm_user_update_from_pwent (user, pwent);
+                g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), g_object_ref (user));
+        }
+
+        if (include_users != NULL) {
+                GSList *l;
+                for (l = include_users; l != NULL; l = l->next) {
+                        struct passwd *pwent;
+
+                        get_pwent_for_name (l->data, &pwent);
+                        if (pwent == NULL) {
+                                continue;
+                        }
+
+                        user = g_hash_table_lookup (new_users_by_name, pwent->pw_name);
+                        if (user != NULL) {
+                                /* already there */
+                                continue;
+                        }
+
+                        user = g_hash_table_lookup (current_users_by_name, pwent->pw_name);
+                        if (user == NULL) {
+                                user = g_object_new (GDM_TYPE_USER, NULL);
+                        } else {
+                                g_object_ref (user);
+                        }
+                        g_object_freeze_notify (G_OBJECT (user));
+                        _gdm_user_update_from_pwent (user, pwent);
+                        g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), user);
                 }
+        }
 
-                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 (include_all != TRUE) {
+                g_debug ("GdmUserManager: include_all is FALSE");
+        } else {
+                struct passwd *pwent;
 
-                if (user == NULL) {
-                        user = create_user (manager);
-                } else {
-                        g_object_ref (user);
+                g_debug ("GdmUserManager: include_all is TRUE");
+
+                for (pwent = fgetpwent (fp);
+                     pwent != NULL;
+                     pwent = fgetpwent (fp)) {
+
+                        /* Skip users below MinimalUID... */
+                        if (pwent->pw_uid < FALLBACK_MINIMAL_UID) {
+                                continue;
+                        }
+
+                        /* ...And users w/ invalid shells... */
+                        if (pwent->pw_shell == NULL
+                            || !g_hash_table_lookup (valid_shells, pwent->pw_shell)) {
+                                g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
+                                continue;
+                        }
+
+                        /* always exclude the "gdm" user. */
+                        if (strcmp (pwent->pw_name, GDM_USERNAME) == 0) {
+                                continue;
+                        }
+
+                        /* ...And explicitly excluded users */
+                        if (exclude_users != NULL) {
+                                GSList   *found;
+
+                                found = g_slist_find_custom (exclude_users,
+                                                             pwent->pw_name,
+                                                             match_name_cmpfunc);
+                                if (found != NULL) {
+                                        g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
+                                        continue;
+                                }
+                        }
+
+                        user = g_hash_table_lookup (new_users_by_name, pwent->pw_name);
+                        if (user != NULL) {
+                                /* already there */
+                                continue;
+                        }
+
+                        user = g_hash_table_lookup (current_users_by_name, pwent->pw_name);
+                        if (user == NULL) {
+                                user = g_object_new (GDM_TYPE_USER, NULL);
+                        } else {
+                                g_object_ref (user);
+                        }
+
+                        /* Freeze & update users not already in the new list */
+                        g_object_freeze_notify (G_OBJECT (user));
+                        _gdm_user_update_from_pwent (user, pwent);
+                        g_hash_table_insert (new_users_by_name, (char *)gdm_user_get_user_name (user), 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);
+        g_hash_table_iter_init (&iter, new_users_by_name);
+        while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+                GdmUser *user2;
+                user2 = g_hash_table_lookup (current_users_by_name, name);
+                if (user2 == NULL) {
+                        *added_users = g_slist_prepend (*added_users, g_object_ref (user));
                 }
         }
 
         /* 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));
+        g_hash_table_iter_init (&iter, current_users_by_name);
+        while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+                GdmUser *user2;
+                user2 = g_hash_table_lookup (new_users_by_name, name);
+                if (user2 == NULL) {
+                        *removed_users = g_slist_prepend (*removed_users, g_object_ref (user));
                 }
         }
 
@@ -1379,39 +2307,222 @@ reload_passwd (GdmUserManager *manager)
 
         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_hash_table_iter_init (&iter, new_users_by_name);
+        while (g_hash_table_iter_next (&iter, (gpointer *) &name, (gpointer *) &user)) {
+                g_object_thaw_notify (G_OBJECT (user));
+        }
 
-        g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
-        g_slist_free (old_users);
+        g_hash_table_destroy (new_users_by_name);
 }
 
+typedef struct {
+        GdmUserManager *manager;
+        GSList         *exclude_users;
+        GSList         *include_users;
+        gboolean        include_all;
+        GHashTable     *shells;
+        GHashTable     *current_users_by_name;
+        GSList         *added_users;
+        GSList         *removed_users;
+} PasswdData;
+
 static void
-reload_users (GdmUserManager *manager)
+passwd_data_free (PasswdData *data)
+{
+        if (data->manager != NULL) {
+                g_object_unref (data->manager);
+        }
+
+        g_slist_foreach (data->added_users, (GFunc) g_object_unref, NULL);
+        g_slist_free (data->added_users);
+
+        g_slist_foreach (data->removed_users, (GFunc) g_object_unref, NULL);
+        g_slist_free (data->removed_users);
+
+        g_slist_foreach (data->exclude_users, (GFunc) g_free, NULL);
+        g_slist_free (data->exclude_users);
+
+        g_slist_foreach (data->include_users, (GFunc) g_free, NULL);
+        g_slist_free (data->include_users);
+
+        g_slice_free (PasswdData, data);
+}
+
+static gboolean
+reload_passwd_job_done (PasswdData *data)
 {
-        reload_ck_history (manager);
-        reload_passwd (manager);
+        GSList *l;
+
+        g_debug ("GdmUserManager: done reloading passwd file");
+
+        /* Go through and handle added users */
+        for (l = data->added_users; l != NULL; l = l->next) {
+                add_user (data->manager, l->data);
+        }
+
+        /* Go through and handle removed users */
+        for (l = data->removed_users; l != NULL; l = l->next) {
+                remove_user (data->manager, l->data);
+        }
+
+        data->manager->priv->load_passwd_pending = FALSE;
+
+        if (! data->manager->priv->is_loaded) {
+                maybe_set_is_loaded (data->manager);
+
+                if (data->manager->priv->include_all == TRUE) {
+                        monitor_local_users (data->manager);
+                }
+        }
+
+        passwd_data_free (data);
+
+        return FALSE;
 }
 
 static gboolean
-reload_users_timeout (GdmUserManager *manager)
+do_reload_passwd_job (GIOSchedulerJob *job,
+                      GCancellable    *cancellable,
+                      PasswdData      *data)
 {
-        reload_users (manager);
-        manager->priv->reload_id = 0;
+        g_debug ("GdmUserManager: reloading passwd file worker");
+
+        reload_passwd_file (data->shells,
+                            data->exclude_users,
+                            data->include_users,
+                            data->include_all,
+                            data->current_users_by_name,
+                            &data->added_users,
+                            &data->removed_users);
+
+        g_io_scheduler_job_send_to_mainloop_async (job,
+                                                   (GSourceFunc) reload_passwd_job_done,
+                                                   data,
+                                                   NULL);
 
         return FALSE;
 }
 
+static GSList *
+slist_deep_copy (const GSList *list)
+{
+        GSList *retval;
+        GSList *l;
+
+        if (list == NULL)
+                return NULL;
+
+        retval = g_slist_copy ((GSList *) list);
+        for (l = retval; l != NULL; l = l->next) {
+                l->data = g_strdup (l->data);
+        }
+
+        return retval;
+}
+
+static void
+schedule_reload_passwd (GdmUserManager *manager)
+{
+        PasswdData *passwd_data;
+
+        manager->priv->load_passwd_pending = TRUE;
+
+        passwd_data = g_slice_new0 (PasswdData);
+        passwd_data->manager = g_object_ref (manager);
+        passwd_data->shells = manager->priv->shells;
+        passwd_data->exclude_users = slist_deep_copy (manager->priv->exclude_usernames);
+        passwd_data->include_users = slist_deep_copy (manager->priv->include_usernames);
+        passwd_data->include_all = manager->priv->include_all;
+        passwd_data->current_users_by_name = manager->priv->users_by_name;
+        passwd_data->added_users = NULL;
+        passwd_data->removed_users = NULL;
+
+        g_debug ("GdmUserManager: scheduling a passwd file update");
+
+        g_io_scheduler_push_job ((GIOSchedulerJobFunc) do_reload_passwd_job,
+                                 passwd_data,
+                                 NULL,
+                                 G_PRIORITY_DEFAULT,
+                                 NULL);
+}
+
+static void
+load_sessions_from_array (GdmUserManager     *manager,
+                          const char * const *session_ids,
+                          int                 number_of_sessions)
+{
+        int i;
+
+        for (i = 0; i < number_of_sessions; i++) {
+                load_new_session (manager, session_ids[i]);
+        }
+}
+
 static void
-queue_reload_users (GdmUserManager *manager)
+on_get_sessions_finished (DBusGProxy     *proxy,
+                          DBusGProxyCall *call,
+                          GdmUserManager *manager)
 {
-        if (manager->priv->reload_id > 0) {
+        GError         *error;
+        gboolean        res;
+        GPtrArray      *sessions;
+
+        g_assert (manager->priv->get_sessions_call == call);
+
+        error = NULL;
+        sessions = NULL;
+        res = dbus_g_proxy_end_call (proxy,
+                                     call,
+                                     &error,
+                                     GDM_DBUS_TYPE_G_OBJECT_PATH_ARRAY,
+                                     &sessions,
+                                     G_TYPE_INVALID);
+
+        if (! res) {
+                if (error != NULL) {
+                        g_warning ("unable to determine sessions for seat: %s",
+                                   error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("unable to determine sessions for seat");
+                }
+                return;
+        }
+
+        manager->priv->get_sessions_call = NULL;
+        g_assert (sessions->len <= G_MAXINT);
+        load_sessions_from_array (manager,
+                                  (const char * const *) sessions->pdata,
+                                  (int) sessions->len);
+        g_ptr_array_foreach (sessions, (GFunc) g_free, NULL);
+        g_ptr_array_free (sessions, TRUE);
+        maybe_set_is_loaded (manager);
+}
+
+static void
+load_sessions (GdmUserManager *manager)
+{
+        DBusGProxyCall *call;
+
+        if (manager->priv->seat.proxy == NULL) {
+                g_debug ("GdmUserManager: no seat proxy; can't load sessions");
                 return;
         }
 
-        g_signal_emit (G_OBJECT (manager), signals[LOADING_USERS], 0);
-        manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, manager);
+        call = dbus_g_proxy_begin_call (manager->priv->seat.proxy,
+                                        "GetSessions",
+                                        (DBusGProxyCallNotify)
+                                        on_get_sessions_finished,
+                                        manager,
+                                        NULL,
+                                        G_TYPE_INVALID);
+
+        if (call == NULL) {
+                g_warning ("GdmUserManager: failed to make GetSessions call");
+                return;
+        }
+
+        manager->priv->get_sessions_call = call;
 }
 
 static void
@@ -1439,6 +2550,109 @@ reload_shells (GdmUserManager *manager)
 }
 
 static void
+load_users_manually (GdmUserManager *manager)
+{
+        gboolean res;
+
+        manager->priv->shells = g_hash_table_new_full (g_str_hash,
+                                                       g_str_equal,
+                                                       g_free,
+                                                       NULL);
+        reload_shells (manager);
+
+        load_sessions (manager);
+
+        res = load_ck_history (manager);
+        schedule_reload_passwd (manager);
+}
+
+static void
+load_users (GdmUserManager *manager)
+{
+        g_assert (manager->priv->accounts_proxy != NULL);
+        g_debug ("GdmUserManager: calling 'ListCachedUsers'");
+
+        dbus_g_proxy_begin_call (manager->priv->accounts_proxy,
+                                 "ListCachedUsers",
+                                 on_list_cached_users_finished,
+                                 manager,
+                                 NULL,
+                                 G_TYPE_INVALID);
+        manager->priv->listing_cached_users = TRUE;
+}
+
+static void
+load_seat_incrementally (GdmUserManager *manager)
+{
+        g_assert (manager->priv->seat.proxy == NULL);
+
+        switch (manager->priv->seat.state) {
+        case GDM_USER_MANAGER_SEAT_STATE_GET_SESSION_ID:
+                get_current_session_id (manager);
+                break;
+        case GDM_USER_MANAGER_SEAT_STATE_GET_ID:
+                get_seat_id_for_current_session (manager);
+                break;
+        case GDM_USER_MANAGER_SEAT_STATE_GET_PROXY:
+                get_seat_proxy (manager);
+                break;
+        case GDM_USER_MANAGER_SEAT_STATE_LOADED:
+                break;
+        default:
+                g_assert_not_reached ();
+        }
+
+        if (manager->priv->seat.state == GDM_USER_MANAGER_SEAT_STATE_LOADED) {
+                gboolean res;
+
+                load_sessions (manager);
+                res = load_ck_history (manager);
+        }
+
+        maybe_set_is_loaded (manager);
+}
+
+static gboolean
+load_idle (GdmUserManager *manager)
+{
+        manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED + 1;
+        load_seat_incrementally (manager);
+        load_users (manager);
+        manager->priv->load_id = 0;
+
+        return FALSE;
+}
+
+static void
+queue_load_seat_and_users (GdmUserManager *manager)
+{
+        if (manager->priv->load_id > 0) {
+                return;
+        }
+
+        manager->priv->load_id = g_idle_add ((GSourceFunc)load_idle, manager);
+}
+
+static gboolean
+reload_passwd_idle (GdmUserManager *manager)
+{
+        schedule_reload_passwd (manager);
+        manager->priv->reload_passwd_id = 0;
+
+        return FALSE;
+}
+
+static void
+queue_reload_passwd (GdmUserManager *manager)
+{
+        if (manager->priv->reload_passwd_id > 0) {
+                g_source_remove (manager->priv->reload_passwd_id);
+        }
+
+        manager->priv->reload_passwd_id = g_timeout_add_seconds (RELOAD_PASSWD_THROTTLE_SECS, (GSourceFunc)reload_passwd_idle, manager);
+}
+
+static void
 on_shells_monitor_changed (GFileMonitor     *monitor,
                            GFile            *file,
                            GFile            *other_file,
@@ -1451,7 +2665,7 @@ on_shells_monitor_changed (GFileMonitor     *monitor,
         }
 
         reload_shells (manager);
-        reload_passwd (manager);
+        queue_reload_passwd (manager);
 }
 
 static void
@@ -1466,29 +2680,140 @@ on_passwd_monitor_changed (GFileMonitor     *monitor,
                 return;
         }
 
-        reload_passwd (manager);
+        queue_reload_passwd (manager);
 }
 
 static void
-ignore_log_handler (const char     *log_domain,
-                    GLogLevelFlags  log_level,
-                    const char     *message,
-                    gpointer        user_data)
+gdm_user_manager_get_property (GObject        *object,
+                               guint           prop_id,
+                               GValue         *value,
+                               GParamSpec     *pspec)
 {
-        return;
+        GdmUserManager *manager;
+
+        manager = GDM_USER_MANAGER (object);
+
+        switch (prop_id) {
+        case PROP_IS_LOADED:
+                g_value_set_boolean (value, manager->priv->is_loaded);
+                break;
+        case PROP_HAS_MULTIPLE_USERS:
+                g_value_set_boolean (value, manager->priv->has_multiple_users);
+                break;
+        case PROP_INCLUDE_ALL:
+                g_value_set_boolean (value, manager->priv->include_all);
+                break;
+        case PROP_INCLUDE_USERNAMES_LIST:
+                g_value_set_pointer (value, manager->priv->include_usernames);
+                break;
+        case PROP_EXCLUDE_USERNAMES_LIST:
+                g_value_set_pointer (value, manager->priv->exclude_usernames);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
+}
+
+static void
+set_include_usernames (GdmUserManager *manager,
+                       GSList         *list)
+{
+        if (manager->priv->include_usernames != NULL) {
+                g_slist_foreach (manager->priv->include_usernames, (GFunc) g_free, NULL);
+                g_slist_free (manager->priv->include_usernames);
+        }
+        manager->priv->include_usernames = slist_deep_copy (list);
+}
+
+static void
+set_exclude_usernames (GdmUserManager *manager,
+                       GSList         *list)
+{
+        if (manager->priv->exclude_usernames != NULL) {
+                g_slist_foreach (manager->priv->exclude_usernames, (GFunc) g_free, NULL);
+                g_slist_free (manager->priv->exclude_usernames);
+        }
+        manager->priv->exclude_usernames = slist_deep_copy (list);
+}
+
+static void
+set_include_all (GdmUserManager *manager,
+                 gboolean        all)
+{
+        if (manager->priv->include_all != all) {
+                manager->priv->include_all = all;
+        }
+}
+
+static void
+gdm_user_manager_set_property (GObject        *object,
+                               guint           prop_id,
+                               const GValue   *value,
+                               GParamSpec     *pspec)
+{
+        GdmUserManager *self;
+
+        self = GDM_USER_MANAGER (object);
+
+        switch (prop_id) {
+        case PROP_INCLUDE_ALL:
+                set_include_all (self, g_value_get_boolean (value));
+                break;
+        case PROP_INCLUDE_USERNAMES_LIST:
+                set_include_usernames (self, g_value_get_pointer (value));
+                break;
+        case PROP_EXCLUDE_USERNAMES_LIST:
+                set_exclude_usernames (self, g_value_get_pointer (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                break;
+        }
 }
 
 static void
-check_gdm_debug (void)
+monitor_local_users (GdmUserManager *manager)
 {
-        GDebugKey gdmkeys[] = { { "gdm", 1 } };
-        const char *gnome_shell_debug = g_getenv ("GNOME_SHELL_DEBUG");
+        GFile *file;
+        GError *error;
+
+        g_debug ("GdmUserManager: Monitoring local users");
 
-        if (!gnome_shell_debug ||
-            !g_parse_debug_string (gnome_shell_debug, gdmkeys, 1)) {
-                g_log_set_handler (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG,
-                                   ignore_log_handler, NULL);
+        /* /etc/shells */
+        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 */
+        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);
 }
 
 static void
@@ -1497,23 +2822,44 @@ gdm_user_manager_class_init (GdmUserManagerClass *klass)
         GObjectClass   *object_class = G_OBJECT_CLASS (klass);
 
         object_class->finalize = gdm_user_manager_finalize;
+        object_class->get_property = gdm_user_manager_get_property;
+        object_class->set_property = gdm_user_manager_set_property;
+
+        g_object_class_install_property (object_class,
+                                         PROP_IS_LOADED,
+                                         g_param_spec_boolean ("is-loaded",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READABLE));
+        g_object_class_install_property (object_class,
+                                         PROP_HAS_MULTIPLE_USERS,
+                                         g_param_spec_boolean ("has-multiple-users",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READABLE));
+        g_object_class_install_property (object_class,
+                                         PROP_INCLUDE_ALL,
+                                         g_param_spec_boolean ("include-all",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READWRITE));
+        g_object_class_install_property (object_class,
+                                         PROP_INCLUDE_USERNAMES_LIST,
+                                         g_param_spec_pointer ("include-usernames-list",
+                                                               NULL,
+                                                               NULL,
+                                                               G_PARAM_READWRITE));
+
+        g_object_class_install_property (object_class,
+                                         PROP_EXCLUDE_USERNAMES_LIST,
+                                         g_param_spec_pointer ("exclude-usernames-list",
+                                                               NULL,
+                                                               NULL,
+                                                               G_PARAM_READWRITE));
 
-        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),
@@ -1538,26 +2884,40 @@ gdm_user_manager_class_init (GdmUserManagerClass *klass)
                               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",
+        signals [USER_CHANGED] =
+                g_signal_new ("user-changed",
                               G_TYPE_FROM_CLASS (klass),
                               G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GdmUserManagerClass, user_login_frequency_changed),
+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_changed),
                               NULL, NULL,
                               g_cclosure_marshal_VOID__OBJECT,
                               G_TYPE_NONE, 1, GDM_TYPE_USER);
 
         g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
-        check_gdm_debug ();
+}
+
+/**
+ * gdm_user_manager_queue_load:
+ * @manager: a #GdmUserManager
+ *
+ * Queue loading users into user manager. This must be called, and the
+ * #GdmUserManager:is-loaded property must be %TRUE before calling
+ * gdm_user_manager_list_users()
+ */
+void
+gdm_user_manager_queue_load (GdmUserManager *manager)
+{
+        g_return_if_fail (GDM_IS_USER_MANAGER (manager));
+
+        if (! manager->priv->is_loaded) {
+                queue_load_seat_and_users (manager);
+        }
 }
 
 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);
 
@@ -1567,73 +2927,41 @@ gdm_user_manager_init (GdmUserManager *manager)
                                                          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));
-        }
+        /* users */
+        manager->priv->users_by_name = g_hash_table_new_full (g_str_hash,
+                                                              g_str_equal,
+                                                              g_free,
+                                                              g_object_unref);
 
-        /* /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);
+        manager->priv->users_by_object_path = g_hash_table_new_full (g_str_hash,
+                                                                     g_str_equal,
+                                                                     NULL,
+                                                                     g_object_unref);
 
+        g_assert (manager->priv->seat.proxy == NULL);
 
-        get_seat_proxy (manager);
+        error = NULL;
+        manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+        if (manager->priv->connection == NULL) {
+                if (error != NULL) {
+                        g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
+                        g_error_free (error);
+                } else {
+                        g_warning ("Failed to connect to the D-Bus daemon");
+                }
+                return;
+        }
 
-        queue_reload_users (manager);
+        get_accounts_proxy (manager);
 
-        manager->priv->users_dirty = FALSE;
+        manager->priv->seat.state = GDM_USER_MANAGER_SEAT_STATE_UNLOADED;
 }
 
 static void
 gdm_user_manager_finalize (GObject *object)
 {
         GdmUserManager *manager;
+        GSList         *node;
 
         g_return_if_fail (object != NULL);
         g_return_if_fail (GDM_IS_USER_MANAGER (object));
@@ -1642,8 +2970,51 @@ gdm_user_manager_finalize (GObject *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_pid > 0) {
+                g_debug ("Killing ck-history process");
+                signal_pid (manager->priv->ck_history_pid, SIGTERM);
+        }
+
+        g_slist_foreach (manager->priv->new_sessions,
+                         (GFunc) unload_new_session, NULL);
+        g_slist_free (manager->priv->new_sessions);
+
+        g_slist_foreach (manager->priv->fetch_user_requests,
+                         (GFunc) free_fetch_user_request, NULL);
+        g_slist_free (manager->priv->fetch_user_requests);
+
+        node = manager->priv->new_users;
+        while (node != NULL) {
+                GdmUser *user;
+                GSList  *next_node;
+
+                user = GDM_USER (node->data);
+                next_node = node->next;
+
+                g_signal_handlers_disconnect_by_func (user, on_new_user_loaded, manager);
+                g_object_unref (user);
+                manager->priv->new_users = g_slist_delete_link (manager->priv->new_users, node);
+                node = next_node;
+        }
+
+        unload_seat (manager);
+
+        if (manager->priv->exclude_usernames != NULL) {
+                g_slist_foreach (manager->priv->exclude_usernames, (GFunc) g_free, NULL);
+                g_slist_free (manager->priv->exclude_usernames);
+        }
+
+        if (manager->priv->include_usernames != NULL) {
+                g_slist_foreach (manager->priv->include_usernames, (GFunc) g_free, NULL);
+                g_slist_free (manager->priv->include_usernames);
+        }
+
+        if (manager->priv->seat.proxy != NULL) {
+                g_object_unref (manager->priv->seat.proxy);
+        }
+
+        if (manager->priv->accounts_proxy != NULL) {
+                g_object_unref (manager->priv->accounts_proxy);
         }
 
         if (manager->priv->ck_history_id != 0) {
@@ -1651,20 +3022,37 @@ gdm_user_manager_finalize (GObject *object)
                 manager->priv->ck_history_id = 0;
         }
 
-        if (manager->priv->reload_id > 0) {
-                g_source_remove (manager->priv->reload_id);
-                manager->priv->reload_id = 0;
+        if (manager->priv->ck_history_watchdog_id != 0) {
+                g_source_remove (manager->priv->ck_history_watchdog_id);
+                manager->priv->ck_history_watchdog_id = 0;
+        }
+
+        if (manager->priv->load_id > 0) {
+                g_source_remove (manager->priv->load_id);
+                manager->priv->load_id = 0;
+        }
+
+        if (manager->priv->reload_passwd_id > 0) {
+                g_source_remove (manager->priv->reload_passwd_id);
+                manager->priv->reload_passwd_id = 0;
         }
 
         g_hash_table_destroy (manager->priv->sessions);
 
-        g_file_monitor_cancel (manager->priv->passwd_monitor);
-        g_hash_table_destroy (manager->priv->users);
+        if (manager->priv->passwd_monitor != NULL) {
+                g_file_monitor_cancel (manager->priv->passwd_monitor);
+        }
+
+        g_hash_table_destroy (manager->priv->users_by_name);
+        g_hash_table_destroy (manager->priv->users_by_object_path);
 
-        g_file_monitor_cancel (manager->priv->shells_monitor);
-        g_hash_table_destroy (manager->priv->shells);
+        if (manager->priv->shells_monitor != NULL) {
+                g_file_monitor_cancel (manager->priv->shells_monitor);
+        }
 
-        g_free (manager->priv->seat_id);
+        if (manager->priv->shells != NULL) {
+                g_hash_table_destroy (manager->priv->shells);
+        }
 
         G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
 }
@@ -1672,7 +3060,11 @@ gdm_user_manager_finalize (GObject *object)
 /**
  * gdm_user_manager_ref_default:
  *
- * Returns: (transfer full): The singleton #GdmUserManager object.
+ * Queue loading users into user manager. This must be called, and the
+ * #GdmUserManager:is-loaded property must be %TRUE before calling
+ * gdm_user_manager_list_users()
+ *
+ * Returns: (transfer full): user manager object
  */
 GdmUserManager *
 gdm_user_manager_ref_default (void)
diff --git a/src/gdmuser/gdm-user-manager.h b/src/gdmuser/gdm-user-manager.h
index f57b4cd..8dd9ede 100644
--- a/src/gdmuser/gdm-user-manager.h
+++ b/src/gdmuser/gdm-user-manager.h
@@ -18,8 +18,8 @@
  *
  */
 
-#ifndef __GDM_USER_MANAGER_H
-#define __GDM_USER_MANAGER_H
+#ifndef __GDM_USER_MANAGER_H__
+#define __GDM_USER_MANAGER_H__
 
 #include <glib-object.h>
 
@@ -35,34 +35,35 @@ G_BEGIN_DECLS
 #define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
 
 typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
+typedef struct GdmUserManager GdmUserManager;
+typedef struct GdmUserManagerClass GdmUserManagerClass;
+typedef enum GdmUserManagerError GdmUserManagerError;
 
-typedef struct
+struct GdmUserManager
 {
         GObject                parent;
         GdmUserManagerPrivate *priv;
-} GdmUserManager;
+};
 
-typedef struct
+struct GdmUserManagerClass
 {
         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;
+        void          (* user_changed)              (GdmUserManager *user_manager,
+                                                     GdmUser        *user);
+};
 
-typedef enum
+enum GdmUserManagerError
 {
         GDM_USER_MANAGER_ERROR_GENERAL,
         GDM_USER_MANAGER_ERROR_KEY_NOT_FOUND
-} GdmUserManagerError;
+};
 
 #define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
 
@@ -71,6 +72,7 @@ GType               gdm_user_manager_get_type              (void);
 
 GdmUserManager *    gdm_user_manager_ref_default           (void);
 
+void                gdm_user_manager_queue_load            (GdmUserManager *manager);
 GSList *            gdm_user_manager_list_users            (GdmUserManager *manager);
 GdmUser *           gdm_user_manager_get_user              (GdmUserManager *manager,
                                                             const char     *username);
@@ -80,6 +82,8 @@ GdmUser *           gdm_user_manager_get_user_by_uid       (GdmUserManager *mana
 gboolean            gdm_user_manager_activate_user_session (GdmUserManager *manager,
                                                             GdmUser        *user);
 
+gboolean            gdm_user_manager_can_switch            (GdmUserManager *manager);
+
 gboolean            gdm_user_manager_goto_login_session    (GdmUserManager *manager);
 
 G_END_DECLS
diff --git a/src/gdmuser/gdm-user-private.h b/src/gdmuser/gdm-user-private.h
index d716f3e..05bda55 100644
--- a/src/gdmuser/gdm-user-private.h
+++ b/src/gdmuser/gdm-user-private.h
@@ -21,8 +21,8 @@
  * Private interfaces to the GdmUser object
  */
 
-#ifndef __GDM_USER_PRIVATE_H
-#define __GDM_USER_PRIVATE_H
+#ifndef __GDM_USER_PRIVATE_H_
+#define __GDM_USER_PRIVATE_H_
 
 #include <pwd.h>
 
@@ -30,15 +30,20 @@
 
 G_BEGIN_DECLS
 
-void _gdm_user_update           (GdmUser             *user,
-                                 const struct passwd *pwent);
+void _gdm_user_update_from_object_path (GdmUser    *user,
+                                        const char *object_path);
+
+void _gdm_user_update_from_pwent (GdmUser             *user,
+                                  const struct passwd *pwent);
+
+void _gdm_user_update_login_frequency (GdmUser *user,
+                                       guint64  login_frequency);
+
 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_H */
+#endif /* !__GDM_USER_PRIVATE__ */
diff --git a/src/gdmuser/gdm-user.c b/src/gdmuser/gdm-user.c
index bd73b95..710510a 100644
--- a/src/gdmuser/gdm-user.c
+++ b/src/gdmuser/gdm-user.c
@@ -26,12 +26,13 @@
 #include <sys/stat.h>
 #include <unistd.h>
 
-/* Note on sync with gdm; need to use -lib here */
-#include <glib/gi18n-lib.h>
+#include <dbus/dbus-glib.h>
+
+#include <glib.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))
@@ -39,25 +40,18 @@
 #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
+
+#define ACCOUNTS_NAME           "org.freedesktop.Accounts"
+#define ACCOUNTS_USER_INTERFACE "org.freedesktop.Accounts.User"
 
 enum {
         PROP_0,
-        PROP_MANAGER,
-        PROP_REAL_NAME,
-        PROP_USER_NAME,
-        PROP_UID,
-        PROP_HOME_DIR,
-        PROP_SHELL,
-        PROP_LOGIN_FREQUENCY,
+        PROP_IS_LOADED
 };
 
 enum {
-        ICON_CHANGED,
+        CHANGED,
         SESSIONS_CHANGED,
         LAST_SIGNAL
 };
@@ -65,28 +59,29 @@ enum {
 struct _GdmUser {
         GObject         parent;
 
-        GdmUserManager *manager;
+        DBusGConnection *connection;
+        DBusGProxy      *accounts_proxy;
+        DBusGProxy      *object_proxy;
+        DBusGProxyCall  *get_all_call;
+        char            *object_path;
 
         uid_t           uid;
         char           *user_name;
         char           *real_name;
-        char           *home_dir;
-        char           *shell;
+        char           *icon_file;
         GList          *sessions;
         gulong          login_frequency;
 
-        GFileMonitor   *icon_monitor;
+        guint           is_loaded : 1;
 };
 
 struct _GdmUserClass
 {
         GObjectClass parent_class;
-
-        void (* icon_changed)     (GdmUser *user);
-        void (* sessions_changed) (GdmUser *user);
 };
 
 static void gdm_user_finalize     (GObject      *object);
+static gboolean check_user_file (const char *filename);
 
 static guint signals[LAST_SIGNAL] = { 0 };
 
@@ -150,84 +145,39 @@ gdm_user_get_num_sessions (GdmUser    *user)
         return g_list_length (user->sessions);
 }
 
-/**
- * gdm_user_get_sessions:
- * @user: a #GdmUser
- *
- * Returns: (transfer none) (element-type utf8): Session identifier strings
- */
-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)
+gdm_user_set_property (GObject        *object,
+                       guint           prop_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;
+        switch (prop_id) {
         default:
-                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
 
 static void
-gdm_user_get_property (GObject    *object,
-                       guint       param_id,
-                       GValue     *value,
-                       GParamSpec *pspec)
+gdm_user_get_property (GObject        *object,
+                       guint           prop_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);
+        switch (prop_id) {
+        case PROP_IS_LOADED:
+                g_value_set_boolean (value, user->is_loaded);
                 break;
         default:
-                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
                 break;
         }
 }
@@ -239,70 +189,23 @@ gdm_user_class_init (GdmUserClass *class)
 
         gobject_class = G_OBJECT_CLASS (class);
 
+        gobject_class->finalize = gdm_user_finalize;
         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",
+                                         PROP_IS_LOADED,
+                                         g_param_spec_boolean ("is-loaded",
+                                                               NULL,
+                                                               NULL,
+                                                               FALSE,
+                                                               G_PARAM_READABLE));
+
+        signals [CHANGED] =
+                g_signal_new ("changed",
                               G_TYPE_FROM_CLASS (class),
                               G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GdmUserClass, icon_changed),
+                              0,
                               NULL, NULL,
                               g_cclosure_marshal_VOID__VOID,
                               G_TYPE_NONE, 0);
@@ -310,74 +213,26 @@ gdm_user_class_init (GdmUserClass *class)
                 g_signal_new ("sessions-changed",
                               G_TYPE_FROM_CLASS (class),
                               G_SIGNAL_RUN_LAST,
-                              G_STRUCT_OFFSET (GdmUserClass, sessions_changed),
+                              0,
                               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)
+gdm_user_init (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;
+
+        error = NULL;
+        user->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+        if (user->connection == NULL) {
+                g_warning ("Couldn't connect to system bus: %s", error->message);
+        }
 }
 
 static void
@@ -387,51 +242,81 @@ gdm_user_finalize (GObject *object)
 
         user = GDM_USER (object);
 
-        g_file_monitor_cancel (user->icon_monitor);
-
         g_free (user->user_name);
         g_free (user->real_name);
+        g_free (user->icon_file);
+        g_free (user->object_path);
+
+        if (user->accounts_proxy != NULL) {
+                g_object_unref (user->accounts_proxy);
+        }
+
+        if (user->object_proxy != NULL) {
+                g_object_unref (user->object_proxy);
+        }
+
+        if (user->connection != NULL) {
+                dbus_g_connection_unref (user->connection);
+        }
 
         if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
                 (*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
 }
 
+static void
+set_is_loaded (GdmUser  *user,
+               gboolean  is_loaded)
+{
+        if (user->is_loaded != is_loaded) {
+                user->is_loaded = is_loaded;
+                g_object_notify (G_OBJECT (user), "is-loaded");
+        }
+}
+
 /**
- * _gdm_user_update:
+ * _gdm_user_update_from_pwent:
  * @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)
+_gdm_user_update_from_pwent (GdmUser             *user,
+                             const struct passwd *pwent)
 {
-        gchar *real_name;
+        gchar *real_name = NULL;
+        gboolean changed;
 
         g_return_if_fail (GDM_IS_USER (user));
         g_return_if_fail (pwent != NULL);
+        g_return_if_fail (user->object_path == NULL);
 
-        g_object_freeze_notify (G_OBJECT (user));
+        changed = FALSE;
 
         /* Display Name */
         if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
-                gchar *first_comma;
-                gchar *real_name_utf8;
+                gchar *first_comma = NULL;
+                gchar *valid_utf8_name = NULL;
 
-                real_name_utf8 = g_locale_to_utf8 (pwent->pw_gecos, -1, NULL, NULL, NULL);
+                if (g_utf8_validate (pwent->pw_gecos, -1, NULL)) {
+                        valid_utf8_name = pwent->pw_gecos;
+                        first_comma = strchr (valid_utf8_name, ',');
+                } else {
+                        g_warning ("User %s has invalid UTF-8 in GECOS field. "
+                                   "It would be a good thing to check /etc/passwd.",
+                                   pwent->pw_name ? pwent->pw_name : "");
+                }
 
-                first_comma = strchr (real_name_utf8, ',');
                 if (first_comma) {
-                        real_name = g_strndup (real_name_utf8, first_comma - real_name_utf8);
-                        g_free (real_name_utf8);
+                        real_name = g_strndup (valid_utf8_name,
+                                               (first_comma - valid_utf8_name));
+                } else if (valid_utf8_name) {
+                        real_name = g_strdup (valid_utf8_name);
                 } else {
-                        real_name = real_name_utf8;
+                        real_name = NULL;
                 }
 
-                if (real_name[0] == '\0') {
+                if (real_name && real_name[0] == '\0') {
                         g_free (real_name);
                         real_name = NULL;
                 }
@@ -439,14 +324,10 @@ _gdm_user_update (GdmUser             *user,
                 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)) {
+        if (g_strcmp0 (real_name, user->real_name) != 0) {
                 g_free (user->real_name);
                 user->real_name = real_name;
-                g_object_notify (G_OBJECT (user), "real-name");
+                changed = TRUE;
         } else {
                 g_free (real_name);
         }
@@ -454,60 +335,58 @@ _gdm_user_update (GdmUser             *user,
         /* UID */
         if (pwent->pw_uid != user->uid) {
                 user->uid = pwent->pw_uid;
-                g_object_notify (G_OBJECT (user), "uid");
+                changed = TRUE;
         }
 
         /* 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)) {
+        if (g_strcmp0 (pwent->pw_name, user->user_name) != 0) {
+                g_free (user->icon_file);
+                user->icon_file = NULL;
+                if (pwent->pw_name != NULL) {
+                        gboolean      res;
+
+                        user->icon_file = g_build_filename (GDM_CACHE_DIR, pwent->pw_name, "face", NULL);
+
+                        res = check_user_file (user->icon_file);
+                        if (!res) {
+                                g_free (user->icon_file);
+                                user->icon_file = g_build_filename (GLOBAL_FACEDIR, pwent->pw_name, NULL);
+                        }
+                }
+
                 g_free (user->user_name);
                 user->user_name = g_strdup (pwent->pw_name);
-                g_object_notify (G_OBJECT (user), "user-name");
+                changed = TRUE;
         }
 
-        /* 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);
+        if (!user->is_loaded) {
+                set_is_loaded (user, TRUE);
         }
 
-        /* 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");
+        if (changed) {
+                g_signal_emit (user, signals[CHANGED], 0);
         }
-
-        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.
+ * _gdm_user_update_login_frequency:
+ * @user: the user object to update
+ * @login_frequency: the number of times the user has logged in
  *
- * Since: 1.0
+ * Updates the login frequency of @user
  **/
 void
-_gdm_user_icon_changed (GdmUser *user)
+_gdm_user_update_login_frequency (GdmUser *user,
+                                  guint64  login_frequency)
 {
         g_return_if_fail (GDM_IS_USER (user));
 
-        g_signal_emit (user, signals[ICON_CHANGED], 0);
+        if (login_frequency == user->login_frequency) {
+                return;
+        }
+
+        user->login_frequency = login_frequency;
+        g_signal_emit (user, signals[CHANGED], 0);
 }
 
 /**
@@ -516,10 +395,8 @@ _gdm_user_icon_changed (GdmUser *user)
  *
  * Retrieves the ID of @user.
  *
- * Returns: a pointer to an array of characters which must not be modified or
+ * Returns: (transfer none): a pointer to an array of characters which must not be modified or
  *  freed, or %NULL.
- *
- * Since: 1.0
  **/
 
 gulong
@@ -536,12 +413,10 @@ gdm_user_get_uid (GdmUser *user)
  *
  * Retrieves the display name of @user.
  *
- * Returns: a pointer to an array of characters which must not be modified or
+ * Returns: (transfer none): a pointer to an array of characters which must not be modified or
  *  freed, or %NULL.
- *
- * Since: 1.0
  **/
-G_CONST_RETURN gchar *
+const char *
 gdm_user_get_real_name (GdmUser *user)
 {
         g_return_val_if_fail (GDM_IS_USER (user), NULL);
@@ -555,13 +430,11 @@ gdm_user_get_real_name (GdmUser *user)
  *
  * Retrieves the login name of @user.
  *
- * Returns: a pointer to an array of characters which must not be modified or
+ * Returns: (transfer none): a pointer to an array of characters which must not be modified or
  *  freed, or %NULL.
- *
- * Since: 1.0
  **/
 
-G_CONST_RETURN gchar *
+const char *
 gdm_user_get_user_name (GdmUser *user)
 {
         g_return_val_if_fail (GDM_IS_USER (user), NULL);
@@ -570,45 +443,13 @@ gdm_user_get_user_name (GdmUser *user)
 }
 
 /**
- * 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.
+ * gdm_user_get_login_frequency:
+ * @user: a #GdmUser
  *
- * Returns: a pointer to an array of characters which must not be modified or
- *  freed, or %NULL.
+ * Returns the number of times @user has logged in.
  *
- * 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;
-}
-
+ * Returns: the login frequency
+ */
 gulong
 gdm_user_get_login_frequency (GdmUser *user)
 {
@@ -625,10 +466,36 @@ gdm_user_collate (GdmUser *user1,
         const char *str2;
         gulong      num1;
         gulong      num2;
+        guint       len1;
+        guint       len2;
+
+        g_return_val_if_fail (GDM_IS_USER (user1), 0);
+        g_return_val_if_fail (GDM_IS_USER (user2), 0);
+
+        num1 = user1->login_frequency;
+        num2 = user2->login_frequency;
+
+        if (num1 > num2) {
+                return -1;
+        }
+
+        if (num1 < num2) {
+                return 1;
+        }
 
-        g_return_val_if_fail (user1 == NULL || GDM_IS_USER (user1), 0);
-        g_return_val_if_fail (user2 == NULL || GDM_IS_USER (user2), 0);
 
+        len1 = g_list_length (user1->sessions);
+        len2 = g_list_length (user2->sessions);
+
+        if (len1 > len2) {
+                return -1;
+        }
+
+        if (len1 < len2) {
+                return 1;
+        }
+
+        /* if login frequency is equal try names */
         if (user1->real_name != NULL) {
                 str1 = user1->real_name;
         } else {
@@ -641,18 +508,6 @@ gdm_user_collate (GdmUser *user1,
                 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;
         }
@@ -669,18 +524,11 @@ gdm_user_collate (GdmUser *user1,
 }
 
 static gboolean
-check_user_file (const char *filename,
-                 uid_t       user,
-                 gssize      max_file_size,
-                 gboolean    relax_group,
-                 gboolean    relax_other)
+check_user_file (const char *filename)
 {
+        gssize      max_file_size = MAX_FILE_SIZE;
         struct stat fileinfo;
 
-        if (max_file_size < 0) {
-                max_file_size = G_MAXSIZE;
-        }
-
         /* Exists/Readable? */
         if (stat (filename, &fileinfo) < 0) {
                 return FALSE;
@@ -691,21 +539,6 @@ check_user_file (const char *filename,
                 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;
@@ -714,224 +547,46 @@ check_user_file (const char *filename,
         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)
+rounded_rectangle (cairo_t *cr,
+                   gdouble  aspect,
+                   gdouble  x,
+                   gdouble  y,
+                   gdouble  corner_radius,
+                   gdouble  width,
+                   gdouble  height)
 {
-        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);
-                }
-        }
-
+        gdouble radius;
+        gdouble degrees;
+
+        radius = corner_radius / aspect;
+        degrees = G_PI / 180.0;
+
+        cairo_new_sub_path (cr);
+        cairo_arc (cr,
+                   x + width - radius,
+                   y + radius,
+                   radius,
+                   -90 * degrees,
+                   0 * degrees);
+        cairo_arc (cr,
+                   x + width - radius,
+                   y + height - radius,
+                   radius,
+                   0 * degrees,
+                   90 * degrees);
+        cairo_arc (cr,
+                   x + radius,
+                   y + height - radius,
+                   radius,
+                   90 * degrees,
+                   180 * degrees);
+        cairo_arc (cr,
+                   x + radius,
+                   y + radius,
+                   radius,
+                   180 * degrees,
+                   270 * degrees);
         cairo_close_path (cr);
 }
 
@@ -1065,7 +720,7 @@ frame_pixbuf (GdkPixbuf *source)
 
         w = gdk_pixbuf_get_width (source) + frame_width * 2;
         h = gdk_pixbuf_get_height (source) + frame_width * 2;
-        radius = w / 3.0;
+        radius = w / 10;
 
         dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
                                TRUE,
@@ -1090,9 +745,13 @@ frame_pixbuf (GdkPixbuf *source)
         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);
+        rounded_rectangle (cr,
+                           1.0,
+                           frame_width + 0.5,
+                           frame_width + 0.5,
+                           radius,
+                           w - frame_width * 2 - 1,
+                           h - frame_width * 2 - 1);
         cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
         cairo_fill_preserve (cr);
 
@@ -1110,11 +769,28 @@ frame_pixbuf (GdkPixbuf *source)
 }
 
 /**
+ * gdm_user_is_logged_in:
+ * @user: a #GdmUser
+ *
+ * Returns whether or not #GdmUser is currently logged in.
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean
+gdm_user_is_logged_in (GdmUser *user)
+{
+        return user->sessions != NULL;
+}
+
+/**
  * gdm_user_render_icon:
- * @user: a #GdmUser:
- * @icon_size: icon size in pixels
+ * @user: a #GdmUser
+ * @icon_size: the size to render the icon at
+ *
+ * Returns a #GdkPixbuf of the account icon belonging to @user
+ * at the pixel size specified by @icon_size.
  *
- * Returns: (transfer full): A new icon for the user
+ * Returns: (transfer full): a #GdkPixbuf
  */
 GdkPixbuf *
 gdm_user_render_icon (GdmUser   *user,
@@ -1122,59 +798,40 @@ gdm_user_render_icon (GdmUser   *user,
 {
         GdkPixbuf    *pixbuf;
         GdkPixbuf    *framed;
-        char         *path;
-        char         *tmp;
         gboolean      res;
+        GError       *error;
 
         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;
+        pixbuf = NULL;
+        if (user->icon_file) {
+                res = check_user_file (user->icon_file);
+                if (res) {
+                        pixbuf = gdk_pixbuf_new_from_file_at_size (user->icon_file,
+                                                                   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;
+        error = NULL;
+        pixbuf = gtk_icon_theme_load_icon (gtk_icon_theme_get_default (),
+
+                                           "avatar-default",
+                                           icon_size,
+                                           GTK_ICON_LOOKUP_FORCE_SIZE,
+                                           &error);
+        if (error) {
+                g_warning ("%s", error->message);
+                g_error_free (error);
         }
-        g_free (path);
  out:
 
         if (pixbuf != NULL) {
@@ -1187,3 +844,253 @@ gdm_user_render_icon (GdmUser   *user,
 
         return pixbuf;
 }
+
+/**
+ * gdm_user_get_icon_file:
+ * @user: a #GdmUser
+ *
+ * Returns the path to the account icon belonging to @user.
+ *
+ * Returns: (transfer none): a path to an icon
+ */
+const char *
+gdm_user_get_icon_file (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+        return user->icon_file;
+}
+
+/**
+ * gdm_user_get_object_path:
+ * @user: a #GdmUser
+ *
+ * Returns the user accounts service object path of @user,
+ * or %NULL if @user doesn't have an object path associated
+ * with it.
+ *
+ * Returns: (transfer none): the primary ConsoleKit session id of the user
+ */
+const char *
+gdm_user_get_object_path (GdmUser *user)
+{
+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+        return user->object_path;
+}
+
+/**
+ * gdm_user_get_primary_session_id:
+ * @user: a #GdmUser
+ *
+ * Returns the primary ConsoleKit session id of @user, or %NULL if @user isn't
+ * logged in.
+ *
+ * Returns: (transfer none): the primary ConsoleKit session id of the user
+ */
+const char *
+gdm_user_get_primary_session_id (GdmUser *user)
+{
+        if (!gdm_user_is_logged_in (user)) {
+                g_debug ("User %s is not logged in, so has no primary session",
+                         gdm_user_get_user_name (user));
+                return NULL;
+        }
+
+        /* FIXME: better way to choose? */
+        return user->sessions->data;
+}
+
+static void
+collect_props (const gchar    *key,
+               const GValue   *value,
+               GdmUser        *user)
+{
+        gboolean handled = TRUE;
+
+        if (strcmp (key, "Uid") == 0) {
+                user->uid = g_value_get_uint64 (value);
+        } else if (strcmp (key, "UserName") == 0) {
+                g_free (user->user_name);
+                user->user_name = g_value_dup_string (value);
+        } else if (strcmp (key, "RealName") == 0) {
+                g_free (user->real_name);
+                user->real_name = g_value_dup_string (value);
+        } else if (strcmp (key, "AccountType") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "Email") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "Language") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "Location") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "LoginFrequency") == 0) {
+                user->login_frequency = g_value_get_uint64 (value);
+        } else if (strcmp (key, "IconFile") == 0) {
+                gboolean res;
+
+                g_free (user->icon_file);
+                user->icon_file = g_value_dup_string (value);
+
+                res = check_user_file (user->icon_file);
+                if (!res) {
+                        g_free (user->icon_file);
+                        user->icon_file = g_build_filename (GLOBAL_FACEDIR, user->user_name, NULL);
+                }
+        } else if (strcmp (key, "Locked") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "AutomaticLogin") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "PasswordMode") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "PasswordHint") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "HomeDirectory") == 0) {
+                /* ignore */
+        } else if (strcmp (key, "Shell") == 0) {
+                /* ignore */
+        } else {
+                handled = FALSE;
+        }
+
+        if (!handled) {
+                g_debug ("unhandled property %s", key);
+        }
+}
+
+static void
+on_get_all_finished (DBusGProxy     *proxy,
+                     DBusGProxyCall *call,
+                     GdmUser        *user)
+{
+        GError      *error;
+        GHashTable  *hash_table;
+        gboolean     res;
+
+        g_assert (user->get_all_call == call);
+        g_assert (user->object_proxy == proxy);
+
+        error = NULL;
+        res = dbus_g_proxy_end_call (proxy,
+                                     call,
+                                     &error,
+                                     dbus_g_type_get_map ("GHashTable", G_TYPE_STRING, G_TYPE_VALUE),
+                                     &hash_table,
+                                     G_TYPE_INVALID);
+        user->get_all_call = NULL;
+        user->object_proxy = NULL;
+
+        if (! res) {
+                g_debug ("Error calling GetAll() when retrieving properties for %s: %s",
+                         user->object_path, error->message);
+                g_error_free (error);
+                goto out;
+        }
+        g_hash_table_foreach (hash_table, (GHFunc) collect_props, user);
+        g_hash_table_unref (hash_table);
+
+        if (!user->is_loaded) {
+                set_is_loaded (user, TRUE);
+        }
+
+        g_signal_emit (user, signals[CHANGED], 0);
+
+out:
+        g_object_unref (proxy);
+}
+
+static gboolean
+update_info (GdmUser *user)
+{
+        DBusGProxy     *proxy;
+        DBusGProxyCall *call;
+
+        proxy = dbus_g_proxy_new_for_name (user->connection,
+                                           ACCOUNTS_NAME,
+                                           user->object_path,
+                                           DBUS_INTERFACE_PROPERTIES);
+
+        call = dbus_g_proxy_begin_call (proxy,
+                                        "GetAll",
+                                        (DBusGProxyCallNotify)
+                                        on_get_all_finished,
+                                        user,
+                                        NULL,
+                                        G_TYPE_STRING,
+                                        ACCOUNTS_USER_INTERFACE,
+                                        G_TYPE_INVALID);
+
+        if (call == NULL) {
+                g_warning ("GdmUser: failed to make GetAll call");
+                goto failed;
+        }
+
+        user->get_all_call = call;
+        user->object_proxy = proxy;
+        return TRUE;
+
+failed:
+        if (proxy != NULL) {
+                g_object_unref (proxy);
+        }
+
+        return FALSE;
+}
+
+static void
+changed_handler (DBusGProxy *proxy,
+                 gpointer   *data)
+{
+        GdmUser *user = GDM_USER (data);
+
+        update_info (user);
+}
+
+/**
+ * _gdm_user_update_from_object_path:
+ * @user: the user object to update.
+ * @object_path: the object path of the user to use.
+ *
+ * Updates the properties of @user from the accounts service via
+ * the object path in @object_path.
+ **/
+void
+_gdm_user_update_from_object_path (GdmUser    *user,
+                                   const char *object_path)
+{
+        g_return_if_fail (GDM_IS_USER (user));
+        g_return_if_fail (object_path != NULL);
+        g_return_if_fail (user->object_path == NULL);
+
+        user->object_path = g_strdup (object_path);
+
+        user->accounts_proxy = dbus_g_proxy_new_for_name (user->connection,
+                                                          ACCOUNTS_NAME,
+                                                          user->object_path,
+                                                          ACCOUNTS_USER_INTERFACE);
+        dbus_g_proxy_set_default_timeout (user->accounts_proxy, INT_MAX);
+        dbus_g_proxy_add_signal (user->accounts_proxy, "Changed", G_TYPE_INVALID);
+
+        dbus_g_proxy_connect_signal (user->accounts_proxy, "Changed",
+                                     G_CALLBACK (changed_handler), user, NULL);
+
+        if (!update_info (user)) {
+                g_warning ("Couldn't update info for user with object path %s", object_path);
+        }
+}
+
+/**
+ * gdm_user_is_loaded:
+ * @user: a #GdmUser
+ *
+ * Determines whether or not the user object is loaded and ready to read from.
+ * #GdmUserManager:is-loaded property must be %TRUE before calling
+ * gdm_user_manager_list_users()
+ *
+ * Returns: %TRUE or %FALSE
+ */
+gboolean
+gdm_user_is_loaded (GdmUser *user)
+{
+        return user->is_loaded;
+}
diff --git a/src/gdmuser/gdm-user.h b/src/gdmuser/gdm-user.h
index f9063e2..2f2616b 100644
--- a/src/gdmuser/gdm-user.h
+++ b/src/gdmuser/gdm-user.h
@@ -22,8 +22,8 @@
  * Facade object for user data, owned by GdmUserManager
  */
 
-#ifndef __GDM_USER_H
-#define __GDM_USER_H
+#ifndef __GDM_USER_H__
+#define __GDM_USER_H__
 
 #include <sys/types.h>
 #include <gtk/gtk.h>
@@ -40,20 +40,24 @@ typedef struct _GdmUserClass GdmUserClass;
 
 GType                 gdm_user_get_type            (void) G_GNUC_CONST;
 
+GdmUser              *gdm_user_new_from_object_path (const char *path);
+const char           *gdm_user_get_object_path      (GdmUser *user);
+
 gulong                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);
+const char           *gdm_user_get_user_name       (GdmUser   *user);
+const char           *gdm_user_get_real_name       (GdmUser   *user);
 guint                 gdm_user_get_num_sessions    (GdmUser   *user);
-GList                *gdm_user_get_sessions        (GdmUser   *user);
+gboolean              gdm_user_is_logged_in        (GdmUser   *user);
 gulong                gdm_user_get_login_frequency (GdmUser   *user);
+const char           *gdm_user_get_icon_file       (GdmUser   *user);
+const char           *gdm_user_get_primary_session_id (GdmUser *user);
 
 GdkPixbuf            *gdm_user_render_icon         (GdmUser   *user,
                                                     gint       icon_size);
 
 gint                  gdm_user_collate             (GdmUser   *user1,
                                                     GdmUser   *user2);
+gboolean              gdm_user_is_loaded           (GdmUser *user);
 
 G_END_DECLS
 



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]