summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWilliam Jon McCann <mccann@jhu.edu>2007-10-03 04:16:41 +0000
committerWilliam Jon McCann <mccann@src.gnome.org>2007-10-03 04:16:41 +0000
commit7b2080c213c2fdea9269e76da72766aa077dd5b7 (patch)
treeb5f48824ae132199eb212b419bffb4472573975c
parent33c755a0078df7031ec342487203dbe3bf6c9f3f (diff)
downloadgdm-7b2080c213c2fdea9269e76da72766aa077dd5b7.tar.gz
Add basic user monitoring. Hook up to user-list.
2007-10-03 William Jon McCann <mccann@jhu.edu> * configure.ac: * gui/simple-greeter/Makefile.am: * gui/simple-greeter/gdm-user-chooser-widget.c: (populate_model), (on_user_added), (on_user_removed), (gdm_user_chooser_widget_init): * gui/simple-greeter/gdm-user-manager.c: (gdm_user_manager_error_quark), (gdm_user_manager_get_user), (listify_hash_values_hfunc), (gdm_user_manager_list_users), (reload_passwd), (reload_shells), (shells_monitor_cb), (passwd_monitor_cb), (gdm_user_manager_class_init), (reload_passwd_timeout), (queue_reload_passwd), (gdm_user_manager_init), (gdm_user_manager_finalize), (gdm_user_manager_ref_default): * gui/simple-greeter/gdm-user-manager.h: * gui/simple-greeter/gdm-user.c: (gdm_user_class_init), (gdm_user_init), (gdm_user_set_property), (gdm_user_get_property), (gdm_user_finalize), (_gdm_user_update), (_gdm_user_add_session), (_gdm_user_remove_session), (_gdm_user_icon_changed), (gdm_user_get_uid), (gdm_user_get_real_name), (gdm_user_get_user_name), (gdm_user_get_home_directory), (gdm_user_get_shell), (gdm_user_get_sessions), (gdm_user_get_n_sessions), (gdm_user_collate): * gui/simple-greeter/gdm-user.h: * gui/simple-greeter/test-user-chooser.c: (main): * gui/simple-greeter/test-user-manager.c: (on_user_added), (on_user_removed), (main): Add basic user monitoring. Hook up to user-list. svn path=/branches/mccann-gobject/; revision=5331
-rw-r--r--ChangeLog29
-rw-r--r--configure.ac5
-rw-r--r--gui/simple-greeter/Makefile.am21
-rw-r--r--gui/simple-greeter/gdm-user-chooser-widget.c122
-rw-r--r--gui/simple-greeter/gdm-user-manager.c482
-rw-r--r--gui/simple-greeter/gdm-user-manager.h73
-rw-r--r--gui/simple-greeter/gdm-user.c587
-rw-r--r--gui/simple-greeter/gdm-user.h59
-rw-r--r--gui/simple-greeter/test-user-chooser.c2
-rw-r--r--gui/simple-greeter/test-user-manager.c82
10 files changed, 1399 insertions, 63 deletions
diff --git a/ChangeLog b/ChangeLog
index 7e6aa159..0d1128e0 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,32 @@
+2007-10-03 William Jon McCann <mccann@jhu.edu>
+
+ * configure.ac:
+ * gui/simple-greeter/Makefile.am:
+ * gui/simple-greeter/gdm-user-chooser-widget.c: (populate_model),
+ (on_user_added), (on_user_removed), (gdm_user_chooser_widget_init):
+ * gui/simple-greeter/gdm-user-manager.c:
+ (gdm_user_manager_error_quark), (gdm_user_manager_get_user),
+ (listify_hash_values_hfunc), (gdm_user_manager_list_users),
+ (reload_passwd), (reload_shells), (shells_monitor_cb),
+ (passwd_monitor_cb), (gdm_user_manager_class_init),
+ (reload_passwd_timeout), (queue_reload_passwd),
+ (gdm_user_manager_init), (gdm_user_manager_finalize),
+ (gdm_user_manager_ref_default):
+ * gui/simple-greeter/gdm-user-manager.h:
+ * gui/simple-greeter/gdm-user.c: (gdm_user_class_init),
+ (gdm_user_init), (gdm_user_set_property), (gdm_user_get_property),
+ (gdm_user_finalize), (_gdm_user_update), (_gdm_user_add_session),
+ (_gdm_user_remove_session), (_gdm_user_icon_changed),
+ (gdm_user_get_uid), (gdm_user_get_real_name),
+ (gdm_user_get_user_name), (gdm_user_get_home_directory),
+ (gdm_user_get_shell), (gdm_user_get_sessions),
+ (gdm_user_get_n_sessions), (gdm_user_collate):
+ * gui/simple-greeter/gdm-user.h:
+ * gui/simple-greeter/test-user-chooser.c: (main):
+ * gui/simple-greeter/test-user-manager.c: (on_user_added),
+ (on_user_removed), (main):
+ Add basic user monitoring. Hook up to user-list.
+
2007-10-02 William Jon McCann <mccann@jhu.edu>
* daemon/gdm-factory-slave.c: (on_session_relay_info),
diff --git a/configure.ac b/configure.ac
index 7c3305e3..8c884053 100644
--- a/configure.ac
+++ b/configure.ac
@@ -26,7 +26,8 @@ GNOME_DOC_INIT
DBUS_GLIB_REQUIRED=0.74
GLIB_REQUIRED=2.13.0
-GTK_REQUIRED=2.6.0
+GTK_REQUIRED=2.10.0
+GNOME_VFS_REQUIRED=2.18.0
PANGO_REQUIRED=1.3.0
LIBGLADE_REQUIRED=1.99.2
SCROLLKEEPER_REQUIRED=0.1.4
@@ -169,7 +170,7 @@ PKG_CHECK_MODULES(DAEMON, gtk+-2.0 >= $GTK_REQUIRED)
AC_SUBST(DAEMON_CFLAGS)
AC_SUBST(DAEMON_LIBS)
-PKG_CHECK_MODULES(GUI, gtk+-2.0 >= $GTK_REQUIRED libglade-2.0 >= $LIBGLADE_REQUIRED)
+PKG_CHECK_MODULES(GUI, gtk+-2.0 >= $GTK_REQUIRED libglade-2.0 >= $LIBGLADE_REQUIRED gnome-vfs-2.0 >= $GNOME_VFS_REQUIRED)
AC_SUBST(GUI_CFLAGS)
AC_SUBST(GUI_LIBS)
diff --git a/gui/simple-greeter/Makefile.am b/gui/simple-greeter/Makefile.am
index a6afa7cf..9d1828a6 100644
--- a/gui/simple-greeter/Makefile.am
+++ b/gui/simple-greeter/Makefile.am
@@ -28,6 +28,7 @@ noinst_PROGRAMS = \
test-language-chooser \
test-session-chooser \
test-user-chooser \
+ test-user-manager \
$(NULL)
test_greeter_background_SOURCES = \
@@ -83,12 +84,28 @@ test_user_chooser_SOURCES = \
gdm-user-chooser-widget.c \
gdm-user-chooser-dialog.h \
gdm-user-chooser-dialog.c \
+ gdm-user-manager.h \
+ gdm-user-manager.c \
+ gdm-user.h \
+ gdm-user.c \
$(NULL)
test_user_chooser_LDADD = \
$(GUI_LIBS) \
$(NULL)
+test_user_manager_SOURCES = \
+ test-user-manager.c \
+ gdm-user-manager.h \
+ gdm-user-manager.c \
+ gdm-user.h \
+ gdm-user.c \
+ $(NULL)
+
+test_user_manager_LDADD = \
+ $(GUI_LIBS) \
+ $(NULL)
+
libexec_PROGRAMS = \
gdm-simple-greeter
@@ -102,6 +119,10 @@ gdm_simple_greeter_SOURCES = \
gdm-greeter-background.c \
gdm-user-chooser-widget.h \
gdm-user-chooser-widget.c \
+ gdm-user-manager.h \
+ gdm-user-manager.c \
+ gdm-user.h \
+ gdm-user.c \
$(NULL)
gdm_simple_greeter_LDADD = \
diff --git a/gui/simple-greeter/gdm-user-chooser-widget.c b/gui/simple-greeter/gdm-user-chooser-widget.c
index 782763ef..525edc18 100644
--- a/gui/simple-greeter/gdm-user-chooser-widget.c
+++ b/gui/simple-greeter/gdm-user-chooser-widget.c
@@ -33,6 +33,7 @@
#include <glib/gstdio.h>
#include <gtk/gtk.h>
+#include "gdm-user-manager.h"
#include "gdm-user-chooser-widget.h"
enum {
@@ -53,6 +54,7 @@ struct GdmUserChooserWidgetPrivate
{
GtkWidget *iconview;
+ GdmUserManager *manager;
GHashTable *available_users;
char *current_user;
};
@@ -372,66 +374,6 @@ static void
populate_model (GdmUserChooserWidget *widget,
GtkTreeModel *model)
{
- GtkTreeIter iter;
- GdkPixbuf *pixbuf;
- char *caption;
- char *tooltip;
-
- /* Add some fake entries */
-
- caption = g_strdup_printf ("<span size=\"x-large\">%s</span>\n<i>%s</i>",
- _("Guest User"),
- _("Already logged in"));
- tooltip = g_strdup_printf ("%s: %s",
- _("Short Name"),
- "guest");
- pixbuf = get_pixbuf_for_user (widget, "guest");
- gtk_list_store_append (GTK_LIST_STORE (model), &iter);
- gtk_list_store_set (GTK_LIST_STORE (model), &iter,
- CHOOSER_LIST_PIXBUF_COLUMN, pixbuf,
- CHOOSER_LIST_CAPTION_COLUMN, caption,
- CHOOSER_LIST_TOOLTIP_COLUMN, tooltip,
- CHOOSER_LIST_ID_COLUMN, "guest",
- -1);
- g_free (caption);
- g_free (tooltip);
-
- caption = g_strdup_printf ("<span size=\"x-large\">%s</span>\n<i>%s</i>",
- _("GNOME Test"),
- _("Already logged in"));
- tooltip = g_strdup_printf ("%s: %s",
- _("Short Name"),
- "gtest");
- pixbuf = get_pixbuf_for_user (widget, "gtest");
- gtk_list_store_append (GTK_LIST_STORE (model), &iter);
- gtk_list_store_set (GTK_LIST_STORE (model), &iter,
- CHOOSER_LIST_PIXBUF_COLUMN, pixbuf,
- CHOOSER_LIST_CAPTION_COLUMN, caption,
- CHOOSER_LIST_TOOLTIP_COLUMN, tooltip,
- CHOOSER_LIST_ID_COLUMN, "gtest",
- -1);
- g_free (caption);
- g_free (tooltip);
-
- caption = g_strdup_printf ("<span size=\"x-large\">%s</span>",
- _("Administrator"));
- tooltip = g_strdup_printf ("%s: %s",
- _("Short Name"),
- "administrator");
-
- gtk_list_store_append (GTK_LIST_STORE (model), &iter);
- gtk_list_store_set (GTK_LIST_STORE (model), &iter,
- CHOOSER_LIST_PIXBUF_COLUMN, pixbuf,
- CHOOSER_LIST_CAPTION_COLUMN, caption,
- CHOOSER_LIST_TOOLTIP_COLUMN, tooltip,
- CHOOSER_LIST_ID_COLUMN, "administrator",
- -1);
- g_free (caption);
- g_free (tooltip);
-
- if (pixbuf != NULL) {
- g_object_unref (pixbuf);
- }
g_hash_table_foreach (widget->priv->available_users,
(GHFunc)add_user_to_model,
@@ -521,6 +463,53 @@ compare_user (GtkTreeModel *model,
}
static void
+on_user_added (GdmUserManager *manager,
+ GdmUser *user,
+ GdmUserChooserWidget *widget)
+{
+ GtkTreeModel *model;
+ GtkTreeIter iter;
+ GdkPixbuf *pixbuf;
+ char *caption;
+ char *tooltip;
+
+ g_debug ("User added: %s", gdm_user_get_user_name (user));
+
+ pixbuf = get_pixbuf_for_user (widget, gdm_user_get_user_name (user));
+
+ caption = g_strdup_printf ("<span size=\"x-large\">%s</span>",
+ gdm_user_get_real_name (user));
+ tooltip = g_strdup_printf ("%s: %s",
+ _("Short Name"),
+ gdm_user_get_user_name (user));
+
+ model = gtk_icon_view_get_model (GTK_ICON_VIEW (widget->priv->iconview));
+ gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+ gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+ CHOOSER_LIST_PIXBUF_COLUMN, pixbuf,
+ CHOOSER_LIST_CAPTION_COLUMN, caption,
+ CHOOSER_LIST_TOOLTIP_COLUMN, tooltip,
+ CHOOSER_LIST_ID_COLUMN, gdm_user_get_user_name (user),
+ -1);
+ g_free (caption);
+ g_free (tooltip);
+
+ if (pixbuf != NULL) {
+ g_object_unref (pixbuf);
+ }
+}
+
+static void
+on_user_removed (GdmUserManager *manager,
+ GdmUser *user,
+ GdmUserChooserWidget *widget)
+{
+ g_debug ("User removed: %s", gdm_user_get_user_name (user));
+
+ /* FIXME: */
+}
+
+static void
gdm_user_chooser_widget_init (GdmUserChooserWidget *widget)
{
GtkTreeModel *model;
@@ -528,13 +517,24 @@ gdm_user_chooser_widget_init (GdmUserChooserWidget *widget)
widget->priv = GDM_USER_CHOOSER_WIDGET_GET_PRIVATE (widget);
+ widget->priv->manager = gdm_user_manager_ref_default ();
+ g_signal_connect (widget->priv->manager,
+ "user-added",
+ G_CALLBACK (on_user_added),
+ widget);
+ g_signal_connect (widget->priv->manager,
+ "user-removed",
+ G_CALLBACK (on_user_removed),
+ widget);
+
widget->priv->available_users = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, (GDestroyNotify)chooser_user_free);
scrolled = gtk_scrolled_window_new (NULL, NULL);
gtk_scrolled_window_set_shadow_type (GTK_SCROLLED_WINDOW (scrolled),
GTK_SHADOW_IN);
gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
- GTK_POLICY_AUTOMATIC, GTK_POLICY_AUTOMATIC);
+ GTK_POLICY_NEVER,
+ GTK_POLICY_AUTOMATIC);
gtk_box_pack_start (GTK_BOX (widget), scrolled, TRUE, TRUE, 0);
widget->priv->iconview = gtk_icon_view_new ();
diff --git a/gui/simple-greeter/gdm-user-manager.c b/gui/simple-greeter/gdm-user-manager.c
new file mode 100644
index 00000000..1755d7d2
--- /dev/null
+++ b/gui/simple-greeter/gdm-user-manager.c
@@ -0,0 +1,482 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+
+#include <libgnomevfs/gnome-vfs-ops.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-user-private.h"
+
+#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
+
+/* Prefs Defaults */
+#define DEFAULT_ALLOW_ROOT TRUE
+#define DEFAULT_MAX_ICON_SIZE 128
+#define DEFAULT_USER_MAX_FILE 65536
+#define DEFAULT_MINIMAL_UID 500
+#define DEFAULT_GLOBAL_FACE_DIR DATADIR "/faces"
+#define DEFAULT_USER_ICON "stock_person"
+#define DEFAULT_EXCLUDE { "bin", \
+ "daemon", \
+ "adm", \
+ "lp", \
+ "sync", \
+ "shutdown", \
+ "halt", \
+ "mail", \
+ "news", \
+ "uucp", \
+ "operator", \
+ "nobody", \
+ "gdm", \
+ "postgres", \
+ "pvm", \
+ "rpm", \
+ "nfsnobody", \
+ "pcap", \
+ NULL }
+
+struct GdmUserManagerPrivate
+{
+ GHashTable *users;
+ GHashTable *shells;
+ GHashTable *exclusions;
+ GnomeVFSMonitorHandle *passwd_monitor;
+ GnomeVFSMonitorHandle *shells_monitor;
+
+ guint reload_id;
+ uid_t minimal_uid;
+
+ guint8 users_dirty : 1;
+};
+
+enum {
+ USER_ADDED,
+ USER_REMOVED,
+ LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_user_manager_class_init (GdmUserManagerClass *klass);
+static void gdm_user_manager_init (GdmUserManager *user_manager);
+static void gdm_user_manager_finalize (GObject *object);
+
+static gpointer user_manager_object = NULL;
+
+G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
+
+GQuark
+gdm_user_manager_error_quark (void)
+{
+ static GQuark ret = 0;
+ if (ret == 0) {
+ ret = g_quark_from_static_string ("gdm_user_manager_error");
+ }
+
+ return ret;
+}
+
+/**
+ * gdm_manager_get_user:
+ * @manager: the manager to query.
+ * @username: the login name of the user to get.
+ *
+ * Retrieves a pointer to the #GdmUser object for the login named @username
+ * from @manager. This pointer is not a reference, and should not be released.
+ *
+ * Returns: a pointer to a #GdmUser object.
+ **/
+GdmUser *
+gdm_user_manager_get_user (GdmUserManager *manager,
+ const char *username)
+{
+ GdmUser *user;
+
+ g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+ g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
+
+ user = g_hash_table_lookup (manager->priv->users, username);
+
+ if (user == NULL) {
+ struct passwd *pwent;
+
+ pwent = getpwnam (username);
+
+ if (pwent != NULL) {
+ user = g_object_new (GDM_TYPE_USER, "manager", manager, NULL);
+ _gdm_user_update (user, pwent);
+ g_hash_table_insert (manager->priv->users,
+ g_strdup (pwent->pw_name),
+ user);
+ g_signal_emit (manager, signals[USER_ADDED], 0, user);
+ }
+ }
+
+ return user;
+}
+
+static void
+listify_hash_values_hfunc (gpointer key,
+ gpointer value,
+ gpointer user_data)
+{
+ GSList **list = user_data;
+
+ *list = g_slist_prepend (*list, value);
+}
+
+GSList *
+gdm_user_manager_list_users (GdmUserManager *manager)
+{
+ GSList *retval;
+
+ g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
+
+ retval = NULL;
+ g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &retval);
+
+ return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
+}
+
+static void
+reload_passwd (GdmUserManager *manager)
+{
+ struct passwd *pwent;
+ GSList *old_users;
+ GSList *new_users;
+ GSList *list;
+
+ old_users = NULL;
+ new_users = NULL;
+
+ 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_n_sessions (list->data)) {
+ g_object_freeze_notify (G_OBJECT (list->data));
+ new_users = g_slist_prepend (new_users, g_object_ref (list->data));
+ }
+ }
+
+ setpwent ();
+
+ for (pwent = getpwent (); pwent; pwent = getpwent ()) {
+ GdmUser *user;
+
+ user = NULL;
+
+ /* Skip users below MinimalUID... */
+ if (pwent->pw_uid < manager->priv->minimal_uid) {
+ continue;
+ }
+
+ /* ...And users w/ invalid shells... */
+ if (!pwent->pw_shell ||
+ !g_hash_table_lookup (manager->priv->shells, pwent->pw_shell)) {
+ continue;
+ }
+
+ /* ...And explicitly excluded users */
+ if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
+ continue;
+ }
+
+ user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
+
+ /* Update users already in the *new* list */
+ if (g_slist_find (new_users, user)) {
+ _gdm_user_update (user, pwent);
+ continue;
+ }
+
+ if (user == NULL) {
+ user = g_object_new (GDM_TYPE_USER,
+ "manager", manager,
+ 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 (user, pwent);
+
+ new_users = g_slist_prepend (new_users, user);
+ }
+
+ endpwent ();
+
+ /* Go through and handle added users */
+ for (list = new_users; list; list = list->next) {
+ if (! g_slist_find (old_users, list->data)) {
+ g_hash_table_insert (manager->priv->users,
+ g_strdup (gdm_user_get_user_name (list->data)),
+ g_object_ref (list->data));
+ g_signal_emit (manager, signals[USER_ADDED], 0, list->data);
+ }
+ }
+
+ /* Go through and handle removed users */
+ for (list = old_users; list; list = list->next) {
+ if (! g_slist_find (new_users, list->data)) {
+ g_signal_emit (manager, signals[USER_REMOVED], 0, list->data);
+ g_hash_table_remove (manager->priv->users,
+ gdm_user_get_user_name (list->data));
+ }
+ }
+
+ /* Cleanup */
+ g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL);
+ g_slist_foreach (new_users, (GFunc) g_object_unref, NULL);
+ g_slist_free (new_users);
+
+ g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
+ g_slist_free (old_users);
+}
+
+static void
+reload_shells (GdmUserManager *manager)
+{
+ char *shell;
+
+ setusershell ();
+
+ g_hash_table_remove_all (manager->priv->shells);
+ for (shell = getusershell (); shell; shell = getusershell ()) {
+ g_hash_table_insert (manager->priv->shells,
+ g_strdup (shell),
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ endusershell ();
+}
+
+static void
+shells_monitor_cb (GnomeVFSMonitorHandle *handle,
+ const gchar *text_uri,
+ const gchar *info_uri,
+ GnomeVFSMonitorEventType event_type,
+ GdmUserManager *manager)
+{
+ if (event_type != GNOME_VFS_MONITOR_EVENT_CHANGED &&
+ event_type != GNOME_VFS_MONITOR_EVENT_CREATED)
+ return;
+
+ reload_shells (manager);
+ reload_passwd (manager);
+}
+
+static void
+passwd_monitor_cb (GnomeVFSMonitorHandle *handle,
+ const gchar *text_uri,
+ const gchar *info_uri,
+ GnomeVFSMonitorEventType event_type,
+ GdmUserManager *manager)
+{
+ if (event_type != GNOME_VFS_MONITOR_EVENT_CHANGED &&
+ event_type != GNOME_VFS_MONITOR_EVENT_CREATED)
+ return;
+
+ reload_passwd (manager);
+}
+
+static void
+gdm_user_manager_class_init (GdmUserManagerClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ object_class->finalize = gdm_user_manager_finalize;
+
+ signals [USER_ADDED] =
+ g_signal_new ("user-added",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, user_added),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDM_TYPE_USER);
+ signals [USER_REMOVED] =
+ g_signal_new ("user-removed",
+ G_TYPE_FROM_CLASS (klass),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserManagerClass, user_removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__OBJECT,
+ G_TYPE_NONE, 1, GDM_TYPE_USER);
+
+ g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
+}
+
+static gboolean
+reload_passwd_timeout (GdmUserManager *manager)
+{
+ reload_passwd (manager);
+ manager->priv->reload_id = 0;
+ return FALSE;
+}
+
+static void
+queue_reload_passwd (GdmUserManager *manager)
+{
+ if (manager->priv->reload_id > 0) {
+ return;
+ }
+
+ manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_passwd_timeout, manager);
+}
+
+static void
+gdm_user_manager_init (GdmUserManager *manager)
+{
+ GError *error;
+ char *uri;
+ GnomeVFSResult result;
+ int i;
+ const char *exclude_default[] = DEFAULT_EXCLUDE;
+
+ manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
+
+ manager->priv->minimal_uid = DEFAULT_MINIMAL_UID;
+
+ /* exclusions */
+ manager->priv->exclusions = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ for (i = 0; exclude_default[i] != NULL; i++) {
+ g_hash_table_insert (manager->priv->exclusions,
+ g_strdup (exclude_default [i]),
+ GUINT_TO_POINTER (TRUE));
+ }
+
+ /* /etc/shells */
+ manager->priv->shells = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ NULL);
+ reload_shells (manager);
+ error = NULL;
+ uri = g_filename_to_uri ("/etc/shells", NULL, &error);
+ if (uri == NULL) {
+ g_critical ("Could not create URI for shells file `/etc/shells': %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ result = gnome_vfs_monitor_add (&(manager->priv->shells_monitor),
+ uri,
+ GNOME_VFS_MONITOR_FILE,
+ (GnomeVFSMonitorCallback)shells_monitor_cb,
+ manager);
+ g_free (uri);
+
+ if (result != GNOME_VFS_OK)
+ g_critical ("Could not install monitor for shells file `/etc/shells': %s",
+ gnome_vfs_result_to_string (result));
+ }
+
+ /* /etc/passwd */
+ manager->priv->users = g_hash_table_new_full (g_str_hash,
+ g_str_equal,
+ g_free,
+ (GDestroyNotify) g_object_run_dispose);
+ error = NULL;
+ uri = g_filename_to_uri ("/etc/passwd", NULL, &error);
+ if (uri == NULL) {
+ g_critical ("Could not create URI for password file `/etc/passwd': %s",
+ error->message);
+ g_error_free (error);
+ } else {
+ result = gnome_vfs_monitor_add (&(manager->priv->passwd_monitor),
+ uri,
+ GNOME_VFS_MONITOR_FILE,
+ (GnomeVFSMonitorCallback)passwd_monitor_cb,
+ manager);
+ g_free (uri);
+
+ if (result != GNOME_VFS_OK)
+ g_critical ("Could not install monitor for password file `/etc/passwd: %s",
+ gnome_vfs_result_to_string (result));
+ }
+
+ /* FIXME: add ConsoleKit seat monitoring */
+
+ queue_reload_passwd (manager);
+
+ manager->priv->users_dirty = FALSE;
+
+}
+
+static void
+gdm_user_manager_finalize (GObject *object)
+{
+ GdmUserManager *manager;
+
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (GDM_IS_USER_MANAGER (object));
+
+ manager = GDM_USER_MANAGER (object);
+
+ g_return_if_fail (manager->priv != NULL);
+
+ if (manager->priv->reload_id > 0) {
+ g_source_remove (manager->priv->reload_id);
+ manager->priv->reload_id = 0;
+ }
+
+ gnome_vfs_monitor_cancel (manager->priv->shells_monitor);
+ g_hash_table_destroy (manager->priv->shells);
+
+ gnome_vfs_monitor_cancel (manager->priv->passwd_monitor);
+ g_hash_table_destroy (manager->priv->users);
+
+ G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
+}
+
+GdmUserManager *
+gdm_user_manager_ref_default (void)
+{
+ if (user_manager_object != NULL) {
+ g_object_ref (user_manager_object);
+ } else {
+ user_manager_object = g_object_new (GDM_TYPE_USER_MANAGER, NULL);
+ g_object_add_weak_pointer (user_manager_object,
+ (gpointer *) &user_manager_object);
+ }
+
+ return GDM_USER_MANAGER (user_manager_object);
+}
diff --git a/gui/simple-greeter/gdm-user-manager.h b/gui/simple-greeter/gdm-user-manager.h
new file mode 100644
index 00000000..590ab1da
--- /dev/null
+++ b/gui/simple-greeter/gdm-user-manager.h
@@ -0,0 +1,73 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ */
+
+#ifndef __GDM_USER_MANAGER_H
+#define __GDM_USER_MANAGER_H
+
+#include <glib-object.h>
+
+#include "gdm-user.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER_MANAGER (gdm_user_manager_get_type ())
+#define GDM_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_MANAGER, GdmUserManager))
+#define GDM_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
+#define GDM_IS_USER_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_MANAGER))
+#define GDM_IS_USER_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_MANAGER))
+#define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
+
+typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
+
+typedef struct
+{
+ GObject parent;
+ GdmUserManagerPrivate *priv;
+} GdmUserManager;
+
+typedef struct
+{
+ GObjectClass parent_class;
+
+ void (* user_added) (GdmUserManager *user_manager,
+ GdmUser *user);
+ void (* user_removed) (GdmUserManager *user_manager,
+ GdmUser *user);
+} GdmUserManagerClass;
+
+typedef enum
+{
+ GDM_USER_MANAGER_ERROR_GENERAL,
+ GDM_USER_MANAGER_ERROR_KEY_NOT_FOUND
+} GdmUserManagerError;
+
+#define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
+
+GQuark gdm_user_manager_error_quark (void);
+GType gdm_user_manager_get_type (void);
+
+GdmUserManager * gdm_user_manager_ref_default (void);
+
+GSList * gdm_user_manager_list_users (GdmUserManager *manager);
+GdmUser * gdm_user_manager_get_user (GdmUserManager *manager,
+ const char *user_name);
+G_END_DECLS
+
+#endif /* __GDM_USER_MANAGER_H */
diff --git a/gui/simple-greeter/gdm-user.c b/gui/simple-greeter/gdm-user.c
new file mode 100644
index 00000000..a7d1e9ed
--- /dev/null
+++ b/gui/simple-greeter/gdm-user.c
@@ -0,0 +1,587 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <glib/gi18n.h>
+#include <gtk/gtkicontheme.h>
+
+#include "gdm-user-manager.h"
+#include "gdm-user-private.h"
+
+#define GDM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_USER, GdmUserClass))
+#define GDM_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_USER))
+#define GDM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), GDM_TYPE_USER, GdmUserClass))
+
+enum {
+ PROP_0,
+ PROP_MANAGER,
+ PROP_REAL_NAME,
+ PROP_USER_NAME,
+ PROP_UID,
+ PROP_HOME_DIR,
+ PROP_SHELL,
+ PROP_SESSIONS
+};
+
+enum {
+ ICON_CHANGED,
+ SESSIONS_CHANGED,
+ LAST_SIGNAL
+};
+
+struct _GdmUser {
+ GObject parent;
+
+ GdmUserManager *manager;
+
+ uid_t uid;
+ gchar *user_name;
+ gchar *real_name;
+ gchar *home_dir;
+ gchar *shell;
+ GSList *sessions;
+};
+
+typedef struct _GdmUserClass
+{
+ GObjectClass parent_class;
+
+ void (* icon_changed) (GdmUser *user);
+ void (* sessions_changed) (GdmUser *user);
+} GdmUserClass;
+
+/* GObject Functions */
+static void gdm_user_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void gdm_user_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void gdm_user_finalize (GObject *object);
+
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GdmUser, gdm_user, G_TYPE_OBJECT);
+
+static void
+gdm_user_class_init (GdmUserClass *class)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (class);
+
+ gobject_class->set_property = gdm_user_set_property;
+ gobject_class->get_property = gdm_user_get_property;
+ gobject_class->finalize = gdm_user_finalize;
+
+ g_object_class_install_property (gobject_class,
+ PROP_MANAGER,
+ g_param_spec_object ("manager",
+ _("Manager"),
+ _("The user manager object this user is controlled by."),
+ GDM_TYPE_USER_MANAGER,
+ (G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY)));
+
+ g_object_class_install_property (gobject_class,
+ PROP_REAL_NAME,
+ g_param_spec_string ("real-name",
+ "Real Name",
+ "The real name to display for this user.",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (gobject_class,
+ PROP_UID,
+ g_param_spec_ulong ("uid",
+ "User ID",
+ "The UID for this user.",
+ 0, G_MAXULONG, 0,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_USER_NAME,
+ g_param_spec_string ("user-name",
+ "User Name",
+ "The login name for this user.",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_HOME_DIR,
+ g_param_spec_string ("home-directory",
+ "Home Directory",
+ "The home directory for this user.",
+ NULL,
+ G_PARAM_READABLE));
+ g_object_class_install_property (gobject_class,
+ PROP_SHELL,
+ g_param_spec_string ("shell",
+ "Shell",
+ "The shell for this user.",
+ NULL,
+ G_PARAM_READABLE));
+
+ signals [ICON_CHANGED] =
+ g_signal_new ("icon-changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserClass, icon_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+ signals [SESSIONS_CHANGED] =
+ g_signal_new ("sessions-changed",
+ G_TYPE_FROM_CLASS (class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (GdmUserClass, sessions_changed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+static void
+gdm_user_init (GdmUser *user)
+{
+ user->manager = NULL;
+ user->user_name = NULL;
+ user->real_name = NULL;
+ user->sessions = NULL;
+}
+
+static void
+gdm_user_set_property (GObject *object,
+ guint param_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ GdmUser *user;
+
+ user = GDM_USER (object);
+
+ switch (param_id) {
+ case PROP_MANAGER:
+ user->manager = g_value_get_object (value);
+ g_assert (user->manager);
+ break;
+
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_user_get_property (GObject *object,
+ guint param_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ GdmUser *user;
+
+ user = GDM_USER (object);
+
+ switch (param_id) {
+ case PROP_MANAGER:
+ g_value_set_object (value, user->manager);
+ break;
+ case PROP_USER_NAME:
+ g_value_set_string (value, user->user_name);
+ break;
+ case PROP_REAL_NAME:
+ g_value_set_string (value, user->real_name);
+ break;
+ case PROP_HOME_DIR:
+ g_value_set_string (value, user->home_dir);
+ break;
+ case PROP_UID:
+ g_value_set_ulong (value, user->uid);
+ break;
+ case PROP_SHELL:
+ g_value_set_string (value, user->shell);
+ break;
+ case PROP_SESSIONS:
+ if (user->sessions) {
+ GValueArray *ar;
+ GSList *list;
+ GValue tmp = { 0 };
+
+ ar = g_value_array_new (g_slist_length (user->sessions));
+ g_value_init (&tmp, GDM_TYPE_USER);
+ for (list = user->sessions; list; list = list->next) {
+ g_value_set_object (&tmp, list->data);
+ g_value_array_append (ar, &tmp);
+ g_value_reset (&tmp);
+ }
+
+ g_value_take_boxed (value, ar);
+ } else {
+ g_value_set_boxed (value, NULL);
+ }
+
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+ break;
+ }
+}
+
+static void
+gdm_user_finalize (GObject *object)
+{
+ GdmUser *user;
+
+ user = GDM_USER (object);
+
+ g_free (user->user_name);
+ g_free (user->real_name);
+
+ if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
+ (*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
+}
+
+/**
+ * _gdm_user_update:
+ * @user: the user object to update.
+ * @pwent: the user data to use.
+ *
+ * Updates the properties of @user using the data in @pwent.
+ *
+ * Since: 1.0
+ **/
+void
+_gdm_user_update (GdmUser *user,
+ const struct passwd *pwent)
+{
+ gchar *real_name;
+
+ g_return_if_fail (GDM_IS_USER (user));
+ g_return_if_fail (pwent != NULL);
+
+ g_object_freeze_notify (G_OBJECT (user));
+
+ /* Display Name */
+ if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
+ gchar *first_comma;
+
+ first_comma = strchr (pwent->pw_gecos, ',');
+ if (first_comma) {
+ real_name = g_strndup (pwent->pw_gecos,
+ (first_comma - pwent->pw_gecos));
+ } else {
+ real_name = g_strdup (pwent->pw_gecos);
+ }
+
+ if (real_name[0] == '\0') {
+ g_free (real_name);
+ real_name = NULL;
+ }
+ } else {
+ real_name = NULL;
+ }
+
+ if ((real_name && !user->real_name) ||
+ (!real_name && user->real_name) ||
+ (real_name &&
+ user->real_name &&
+ strcmp (real_name, user->real_name) != 0)) {
+ g_free (user->real_name);
+ user->real_name = real_name;
+ g_object_notify (G_OBJECT (user), "real-name");
+ } else {
+ g_free (real_name);
+ }
+
+ /* UID */
+ if (pwent->pw_uid != user->uid) {
+ user->uid = pwent->pw_uid;
+ g_object_notify (G_OBJECT (user), "uid");
+ }
+
+ /* Username */
+ if ((pwent->pw_name && !user->user_name) ||
+ (!pwent->pw_name && user->user_name) ||
+ (pwent->pw_name &&
+ user->user_name &&
+ strcmp (user->user_name, pwent->pw_name) != 0)) {
+ g_free (user->user_name);
+ user->user_name = g_strdup (pwent->pw_name);
+ g_object_notify (G_OBJECT (user), "user-name");
+ }
+
+ /* Home Directory */
+ if ((pwent->pw_dir && !user->home_dir) ||
+ (!pwent->pw_dir && user->home_dir) ||
+ strcmp (user->home_dir, pwent->pw_dir) != 0) {
+ g_free (user->home_dir);
+ user->home_dir = g_strdup (pwent->pw_dir);
+ g_object_notify (G_OBJECT (user), "home-directory");
+ g_signal_emit (user, signals[ICON_CHANGED], 0);
+ }
+
+ /* Shell */
+ if ((pwent->pw_shell && !user->shell) ||
+ (!pwent->pw_shell && user->shell) ||
+ (pwent->pw_shell &&
+ user->shell &&
+ strcmp (user->shell, pwent->pw_shell) != 0)) {
+ g_free (user->shell);
+ user->shell = g_strdup (pwent->pw_shell);
+ g_object_notify (G_OBJECT (user), "shell");
+ }
+
+ g_object_thaw_notify (G_OBJECT (user));
+}
+
+void
+_gdm_user_add_session (GdmUser *user,
+ const char *ssid)
+{
+ g_return_if_fail (GDM_IS_USER (user));
+ g_return_if_fail (ssid != NULL);
+
+ if (! g_slist_find (user->sessions, ssid)) {
+ user->sessions = g_slist_append (user->sessions, g_strdup (ssid));
+ g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
+ }
+}
+
+/**
+ * _gdm_user_remove_session:
+ * @user: the user to modify.
+ * @ssid: the session id to remove
+ *
+ * Removes @ssid from the list of sessions @user is using, and emits the
+ * "sessions-changed" signal, if necessary.
+ *
+ * Since: 1.0
+ **/
+void
+_gdm_user_remove_session (GdmUser *user,
+ const char *ssid)
+{
+ GSList *li;
+
+ g_return_if_fail (GDM_IS_USER (user));
+ g_return_if_fail (ssid != NULL);
+
+ li = g_slist_find (user->sessions, ssid);
+ if (li != NULL) {
+ g_free (li->data);
+ user->sessions = g_slist_delete_link (user->sessions, li);
+ g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
+ }
+}
+
+/**
+ * _gdm_user_icon_changed:
+ * @user: the user to emit the signal for.
+ *
+ * Emits the "icon-changed" signal for @user.
+ *
+ * Since: 1.0
+ **/
+void
+_gdm_user_icon_changed (GdmUser *user)
+{
+ g_return_if_fail (GDM_IS_USER (user));
+
+ g_signal_emit (user, signals[ICON_CHANGED], 0);
+}
+
+/**
+ * gdm_user_get_uid:
+ * @user: the user object to examine.
+ *
+ * Retrieves the ID of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+uid_t
+gdm_user_get_uid (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), -1);
+
+ return user->uid;
+}
+
+/**
+ * gdm_user_get_real_name:
+ * @user: the user object to examine.
+ *
+ * Retrieves the display name of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+G_CONST_RETURN gchar *
+gdm_user_get_real_name (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return (user->real_name ? user->real_name : user->user_name);
+}
+
+/**
+ * gdm_user_get_user_name:
+ * @user: the user object to examine.
+ *
+ * Retrieves the login name of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_user_name (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->user_name;
+}
+
+/**
+ * gdm_user_get_home_directory:
+ * @user: the user object to examine.
+ *
+ * Retrieves the home directory of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_home_directory (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->home_dir;
+}
+
+/**
+ * gdm_user_get_shell:
+ * @user: the user object to examine.
+ *
+ * Retrieves the login shell of @user.
+ *
+ * Returns: a pointer to an array of characters which must not be modified or
+ * freed, or %NULL.
+ *
+ * Since: 1.0
+ **/
+
+G_CONST_RETURN gchar *
+gdm_user_get_shell (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return user->shell;
+}
+
+/**
+ * gdm_user_get_displays:
+ * @user: the user object to examine.
+ *
+ * Retrieves a new list of the displays that @user is logged in on. The list
+ * itself must be freed with g_slist_free() when no longer needed.
+ *
+ * Returns: a list of #GdmDisplay objects which must be freed with
+ * g_slist_free().
+ *
+ * Since: 1.0
+ **/
+GSList *
+gdm_user_get_sessions (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), NULL);
+
+ return g_slist_copy (user->sessions);
+}
+
+/**
+ * gdm_user_get_n_sessions:
+ * @user: the user object to examine.
+ *
+ * Retrieves the number of sessions that @user is logged into.
+ *
+ * Returns: an unsigned integer.
+ *
+ * Since: 1.0
+ **/
+guint
+gdm_user_get_n_sessions (GdmUser *user)
+{
+ g_return_val_if_fail (GDM_IS_USER (user), FALSE);
+
+ return g_slist_length (user->sessions);
+}
+
+gint
+gdm_user_collate (GdmUser *user1,
+ GdmUser *user2)
+{
+ const gchar *str1, *str2;
+
+ g_return_val_if_fail (user1 == NULL || GDM_IS_USER (user1), 0);
+ g_return_val_if_fail (user2 == NULL || GDM_IS_USER (user2), 0);
+
+ if (!user1 && user2)
+ return -1;
+
+ if (user1 && !user2)
+ return 1;
+
+ if (!user1 && !user2)
+ return 0;
+
+ if (user1->real_name)
+ str1 = user1->real_name;
+ else
+ str1 = user1->user_name;
+
+ if (user2->real_name)
+ str2 = user2->real_name;
+ else
+ str2 = user2->user_name;
+
+ if (!str1 && str2)
+ return -1;
+
+ if (str1 && !str2)
+ return 1;
+
+ if (!str1 && !str2)
+ return 0;
+
+ return g_utf8_collate (str1, str2);
+}
diff --git a/gui/simple-greeter/gdm-user.h b/gui/simple-greeter/gdm-user.h
new file mode 100644
index 00000000..fb091534
--- /dev/null
+++ b/gui/simple-greeter/gdm-user.h
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*
+ * Facade object for user data, owned by GdmUserManager
+ */
+
+#ifndef __GDM_USER__
+#define __GDM_USER__ 1
+
+#include <sys/types.h>
+#include <gtk/gtkwidget.h>
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_USER (gdm_user_get_type ())
+#define GDM_USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_USER, GdmUser))
+#define GDM_IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_USER))
+
+typedef struct _GdmUser GdmUser;
+
+GType gdm_user_get_type (void) G_GNUC_CONST;
+
+uid_t gdm_user_get_uid (GdmUser *user);
+G_CONST_RETURN gchar *gdm_user_get_user_name (GdmUser *user);
+G_CONST_RETURN gchar *gdm_user_get_real_name (GdmUser *user);
+G_CONST_RETURN gchar *gdm_user_get_home_directory (GdmUser *user);
+G_CONST_RETURN gchar *gdm_user_get_shell (GdmUser *user);
+
+GSList *gdm_user_get_sessions (GdmUser *user);
+guint gdm_user_get_n_sessions (GdmUser *user);
+
+GdkPixbuf *gdm_user_render_icon (GdmUser *user,
+ GtkWidget *widget,
+ gint icon_size);
+
+gint gdm_user_collate (GdmUser *user1,
+ GdmUser *user2);
+
+G_END_DECLS
+
+#endif
diff --git a/gui/simple-greeter/test-user-chooser.c b/gui/simple-greeter/test-user-chooser.c
index d3c9fec2..7efd8c33 100644
--- a/gui/simple-greeter/test-user-chooser.c
+++ b/gui/simple-greeter/test-user-chooser.c
@@ -29,6 +29,7 @@
#include <glib/gi18n.h>
#include <gtk/gtk.h>
+#include <libgnomevfs/gnome-vfs-init.h>
#include "gdm-user-chooser-dialog.h"
@@ -44,6 +45,7 @@ main (int argc, char *argv[])
setlocale (LC_ALL, "");
gtk_init (&argc, &argv);
+ gnome_vfs_init ();
dialog = gdm_user_chooser_dialog_new ();
/*gtk_widget_set_size_request (dialog, 480, 128);*/
diff --git a/gui/simple-greeter/test-user-manager.c b/gui/simple-greeter/test-user-manager.c
new file mode 100644
index 00000000..436dce18
--- /dev/null
+++ b/gui/simple-greeter/test-user-manager.c
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <libintl.h>
+#include <locale.h>
+#include <string.h>
+#include <unistd.h>
+#include <signal.h>
+#include <errno.h>
+
+#include <glib/gi18n.h>
+#include <gtk/gtk.h>
+
+#include <libgnomevfs/gnome-vfs-init.h>
+
+#include "gdm-user-manager.h"
+
+static GdmUserManager *manager = NULL;
+
+static void
+on_user_added (GdmUserManager *manager,
+ GdmUser *user,
+ gpointer data)
+{
+ g_debug ("User added: %s", gdm_user_get_user_name (user));
+}
+
+static void
+on_user_removed (GdmUserManager *manager,
+ GdmUser *user,
+ gpointer data)
+{
+ g_debug ("User removed: %s", gdm_user_get_user_name (user));
+}
+
+int
+main (int argc, char *argv[])
+{
+ GtkWidget *dialog;
+
+ bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ setlocale (LC_ALL, "");
+
+ gtk_init (&argc, &argv);
+ gnome_vfs_init ();
+
+ manager = gdm_user_manager_ref_default ();
+ g_signal_connect (manager,
+ "user-added",
+ G_CALLBACK (on_user_added),
+ NULL);
+ g_signal_connect (manager,
+ "user-removed",
+ G_CALLBACK (on_user_removed),
+ NULL);
+
+ gtk_main ();
+
+ return 0;
+}