source: proiecte/PPPP/gdm/debian/patches/16_gdmserver_user_manager.patch @ 134

Last change on this file since 134 was 134, checked in by (none), 14 years ago

gdm sources with the modifications for webcam

File size: 89.2 KB
RevLine 
[134]1#
2# Description: Add org.gnome.DisplayManager.UserManager interface to gdmserver to be shared between greeter, configuration applet and FUSA applet
3# Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gdm/+bug/423450
4# Upstream: https://bugzilla.gnome.org/show_bug.cgi?id=593996
5#
6From 35aa54f6fe3ff8fa8d6129f805ea243a6204aa65 Mon Sep 17 00:00:00 2001
7From: Robert Ancell <robert.ancell@canonical.com>
8Date: Fri, 11 Sep 2009 16:01:30 +1000
9Subject: [PATCH] Add org.gnome.DisplayManager.UserManager interface to gdmserver
10
11diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/configure.ac gdm-2.28.0.new/configure.ac
12--- gdm-2.28.0/configure.ac     2009-10-14 18:30:50.000000000 +1100
13+++ gdm-2.28.0.new/configure.ac 2009-10-14 18:30:51.000000000 +1100
14@@ -73,6 +73,7 @@
15         polkit-gobject-1 >= $POLKIT_GOBJECT_REQUIRED_VERSION
16         gobject-2.0 >= $GLIB_REQUIRED_VERSION
17         gio-2.0 >= $GLIB_REQUIRED_VERSION
18+        gconf-2.0 >= $GCONF_REQUIRED_VERSION
19         hal
20 )
21 AC_SUBST(DAEMON_CFLAGS)
22@@ -949,6 +950,17 @@
23 fi
24 AC_SUBST(logdir, $GDM_LOG_DIR)
25 
26+AC_ARG_WITH(cache-dir,
27+            AS_HELP_STRING([--with-cache-dir=<file>],
28+                           [cache dir]))
29+
30+if ! test -z "$with_cache_dir"; then
31+   GDM_CACHE_DIR=$with_cache_dir
32+else
33+   GDM_CACHE_DIR=/var/cache/gdm
34+fi
35+AC_SUBST(cachedir, $GDM_CACHE_DIR)
36+
37 withval=""
38 AC_ARG_WITH(at-bindir,
39             AS_HELP_STRING([--with-at-bindir=<PATH>]
40diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/gdm-user.c gdm-2.28.0.new/daemon/gdm-user.c
41--- gdm-2.28.0/daemon/gdm-user.c        1970-01-01 10:00:00.000000000 +1000
42+++ gdm-2.28.0.new/daemon/gdm-user.c    2009-10-14 18:30:51.000000000 +1100
43@@ -0,0 +1,596 @@
44+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
45+ *
46+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
47+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
48+ *
49+ * This program is free software; you can redistribute it and/or modify
50+ * it under the terms of the GNU General Public License as published by
51+ * the Free Software Foundation; either version 2 of the License, or
52+ * (at your option) any later version.
53+ *
54+ * This program is distributed in the hope that it will be useful,
55+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
56+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
57+ * GNU General Public License for more details.
58+ *
59+ * You should have received a copy of the GNU General Public License
60+ * along with this program; if not, write to the Free Software
61+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
62+ */
63+
64+#include <config.h>
65+
66+#include <float.h>
67+#include <string.h>
68+#include <sys/types.h>
69+#include <sys/stat.h>
70+#include <unistd.h>
71+
72+#include <glib/gi18n.h>
73+#include <gio/gio.h>
74+
75+#include "gdm-user-manager.h"
76+#include "gdm-user-private.h"
77+
78+#define GDM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_USER, GdmUserClass))
79+#define GDM_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_USER))
80+#define GDM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), GDM_TYPE_USER, GdmUserClass))
81+
82+enum {
83+        PROP_0,
84+        PROP_REAL_NAME,
85+        PROP_USER_NAME,
86+        PROP_UID,
87+        PROP_HOME_DIR,
88+        PROP_SHELL,
89+        PROP_ICON_URL,       
90+        PROP_LOGIN_FREQUENCY,
91+};
92+
93+enum {
94+        ICON_CHANGED,
95+        SESSIONS_CHANGED,
96+        LAST_SIGNAL
97+};
98+
99+struct _GdmUser {
100+        GObject         parent;
101+
102+        uid_t           uid;
103+        char           *user_name;
104+        char           *real_name;
105+        char           *home_dir;
106+        char           *shell;
107+        char           *icon_url;
108+        GList          *sessions;
109+        gulong          login_frequency;
110+
111+        GFileMonitor   *icon_monitor;
112+};
113+
114+typedef struct _GdmUserClass
115+{
116+        GObjectClass parent_class;
117+
118+        void (* icon_changed)     (GdmUser *user);
119+        void (* sessions_changed) (GdmUser *user);
120+} GdmUserClass;
121+
122+static void gdm_user_finalize     (GObject      *object);
123+
124+static guint signals[LAST_SIGNAL] = { 0 };
125+
126+G_DEFINE_TYPE (GdmUser, gdm_user, G_TYPE_OBJECT)
127+
128+static int
129+session_compare (const char *a,
130+                 const char *b)
131+{
132+        if (a == NULL) {
133+                return 1;
134+        } else if (b == NULL) {
135+                return -1;
136+        }
137+
138+        return strcmp (a, b);
139+}
140+
141+void
142+_gdm_user_add_session (GdmUser    *user,
143+                       const char *ssid)
144+{
145+        GList *li;
146+
147+        g_return_if_fail (GDM_IS_USER (user));
148+        g_return_if_fail (ssid != NULL);
149+
150+        li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
151+        if (li == NULL) {
152+                g_debug ("GdmUser: adding session %s", ssid);
153+                user->sessions = g_list_prepend (user->sessions, g_strdup (ssid));
154+                g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
155+        } else {
156+                g_debug ("GdmUser: session already present: %s", ssid);
157+        }
158+}
159+
160+void
161+_gdm_user_remove_session (GdmUser    *user,
162+                          const char *ssid)
163+{
164+        GList *li;
165+
166+        g_return_if_fail (GDM_IS_USER (user));
167+        g_return_if_fail (ssid != NULL);
168+
169+        li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
170+        if (li != NULL) {
171+                g_debug ("GdmUser: removing session %s", ssid);
172+                g_free (li->data);
173+                user->sessions = g_list_delete_link (user->sessions, li);
174+                g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
175+        } else {
176+                g_debug ("GdmUser: session not found: %s", ssid);
177+        }
178+}
179+
180+guint
181+gdm_user_get_num_sessions (GdmUser    *user)
182+{
183+        return g_list_length (user->sessions);
184+}
185+
186+GList *
187+gdm_user_get_sessions (GdmUser *user)
188+{
189+        return user->sessions;
190+}
191+
192+static void
193+gdm_user_set_property (GObject      *object,
194+                       guint         param_id,
195+                       const GValue *value,
196+                       GParamSpec   *pspec)
197+{
198+        GdmUser *user;
199+
200+        user = GDM_USER (object);
201+
202+        switch (param_id) {
203+        case PROP_LOGIN_FREQUENCY:
204+                user->login_frequency = g_value_get_ulong (value);
205+                g_object_notify (G_OBJECT (user), "login-frequency");
206+                break;
207+        default:
208+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
209+                break;
210+        }
211+}
212+
213+static void
214+gdm_user_get_property (GObject    *object,
215+                       guint       param_id,
216+                       GValue     *value,
217+                       GParamSpec *pspec)
218+{
219+        GdmUser *user;
220+
221+        user = GDM_USER (object);
222+
223+        switch (param_id) {
224+        case PROP_USER_NAME:
225+                g_value_set_string (value, user->user_name);
226+                break;
227+        case PROP_REAL_NAME:
228+                g_value_set_string (value, user->real_name);
229+                break;
230+        case PROP_HOME_DIR:
231+                g_value_set_string (value, user->home_dir);
232+                break;
233+        case PROP_UID:
234+                g_value_set_ulong (value, user->uid);
235+                break;
236+        case PROP_SHELL:
237+                g_value_set_string (value, user->shell);
238+                break;
239+        case PROP_ICON_URL:
240+                g_value_set_string (value, user->icon_url);
241+                break;           
242+        case PROP_LOGIN_FREQUENCY:
243+                g_value_set_ulong (value, user->login_frequency);
244+                break;
245+        default:
246+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
247+                break;
248+        }
249+}
250+
251+static void
252+gdm_user_class_init (GdmUserClass *class)
253+{
254+        GObjectClass *gobject_class;
255+
256+        gobject_class = G_OBJECT_CLASS (class);
257+
258+        gobject_class->set_property = gdm_user_set_property;
259+        gobject_class->get_property = gdm_user_get_property;
260+        gobject_class->finalize = gdm_user_finalize;
261+
262+        g_object_class_install_property (gobject_class,
263+                                         PROP_REAL_NAME,
264+                                         g_param_spec_string ("real-name",
265+                                                              "Real Name",
266+                                                              "The real name to display for this user.",
267+                                                              NULL,
268+                                                              G_PARAM_READABLE));
269+
270+        g_object_class_install_property (gobject_class,
271+                                         PROP_UID,
272+                                         g_param_spec_ulong ("uid",
273+                                                             "User ID",
274+                                                             "The UID for this user.",
275+                                                             0, G_MAXULONG, 0,
276+                                                             G_PARAM_READABLE));
277+        g_object_class_install_property (gobject_class,
278+                                         PROP_USER_NAME,
279+                                         g_param_spec_string ("user-name",
280+                                                              "User Name",
281+                                                              "The login name for this user.",
282+                                                              NULL,
283+                                                              G_PARAM_READABLE));
284+        g_object_class_install_property (gobject_class,
285+                                         PROP_HOME_DIR,
286+                                         g_param_spec_string ("home-directory",
287+                                                              "Home Directory",
288+                                                              "The home directory for this user.",
289+                                                              NULL,
290+                                                              G_PARAM_READABLE));
291+        g_object_class_install_property (gobject_class,
292+                                         PROP_SHELL,
293+                                         g_param_spec_string ("shell",
294+                                                              "Shell",
295+                                                              "The shell for this user.",
296+                                                              NULL,
297+                                                              G_PARAM_READABLE));
298+        g_object_class_install_property (gobject_class,
299+                                         PROP_SHELL,
300+                                         g_param_spec_string ("icon-url",
301+                                                              "Icon URL",
302+                                                              "The icon for this user.",
303+                                                              NULL,
304+                                                              G_PARAM_READABLE));
305+        g_object_class_install_property (gobject_class,
306+                                         PROP_LOGIN_FREQUENCY,
307+                                         g_param_spec_ulong ("login-frequency",
308+                                                             "login frequency",
309+                                                             "login frequency",
310+                                                             0,
311+                                                             G_MAXULONG,
312+                                                             0,
313+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
314+
315+        signals [ICON_CHANGED] =
316+                g_signal_new ("icon-changed",
317+                              G_TYPE_FROM_CLASS (class),
318+                              G_SIGNAL_RUN_LAST,
319+                              G_STRUCT_OFFSET (GdmUserClass, icon_changed),
320+                              NULL, NULL,
321+                              g_cclosure_marshal_VOID__VOID,
322+                              G_TYPE_NONE, 0);
323+        signals [SESSIONS_CHANGED] =
324+                g_signal_new ("sessions-changed",
325+                              G_TYPE_FROM_CLASS (class),
326+                              G_SIGNAL_RUN_LAST,
327+                              G_STRUCT_OFFSET (GdmUserClass, sessions_changed),
328+                              NULL, NULL,
329+                              g_cclosure_marshal_VOID__VOID,
330+                              G_TYPE_NONE, 0);
331+}
332+
333+
334+static void
335+on_icon_monitor_changed (GFileMonitor     *monitor,
336+                         GFile            *file,
337+                         GFile            *other_file,
338+                         GFileMonitorEvent event_type,
339+                         GdmUser          *user)
340+{
341+        g_debug ("Icon changed: %d", event_type);
342+
343+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
344+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
345+                return;
346+        }
347+
348+        g_signal_emit (user, signals[ICON_CHANGED], 0);
349+}
350+
351+static void
352+update_icon_monitor (GdmUser *user)
353+{
354+        GFile  *file;
355+        GError *error = NULL;
356+
357+        if (user->icon_url != NULL) {
358+                g_free (user->icon_url);
359+                user->icon_url = NULL;
360+        }
361+        if (user->home_dir != NULL) {
362+                gchar *filename;
363+                filename = g_build_filename (user->home_dir, ".face", NULL);
364+                user->icon_url = g_strjoin(NULL, "file://", filename, NULL);
365+                g_free (filename);
366+        }
367+        g_object_notify (G_OBJECT (user), "icon-url");
368+
369+        if (user->icon_monitor != NULL) {
370+                g_file_monitor_cancel (user->icon_monitor);
371+                user->icon_monitor = NULL;
372+        }
373+
374+        if (user->icon_url == NULL) {
375+                return;
376+        }
377+
378+        g_debug ("adding monitor for '%s'", user->icon_url);
379+        file = g_file_new_for_uri (user->icon_url);
380+        user->icon_monitor = g_file_monitor_file (file,
381+                                                  G_FILE_MONITOR_NONE,
382+                                                  NULL,
383+                                                  &error);
384+        if (user->icon_monitor != NULL) {
385+                g_signal_connect (user->icon_monitor,
386+                                  "changed",
387+                                  G_CALLBACK (on_icon_monitor_changed),
388+                                  user);
389+        } else {
390+                g_warning ("Unable to monitor %s: %s", user->icon_url, error->message);
391+                g_error_free (error);
392+        }
393+        g_object_unref (file);
394+}
395+
396+static void
397+gdm_user_init (GdmUser *user)
398+{
399+        user->user_name = NULL;
400+        user->real_name = NULL;
401+        user->sessions = NULL;
402+}
403+
404+static void
405+gdm_user_finalize (GObject *object)
406+{
407+        GdmUser *user;
408+
409+        user = GDM_USER (object);
410+
411+        g_file_monitor_cancel (user->icon_monitor);
412+
413+        g_free (user->user_name);
414+        g_free (user->real_name);
415+
416+        if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
417+                (*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
418+}
419+
420+/**
421+ * _gdm_user_update:
422+ * @user: the user object to update.
423+ * @pwent: the user data to use.
424+ *
425+ * Updates the properties of @user using the data in @pwent.
426+ *
427+ * Since: 1.0
428+ **/
429+void
430+_gdm_user_update (GdmUser             *user,
431+                  const struct passwd *pwent)
432+{
433+        gchar *real_name;
434+
435+        g_return_if_fail (GDM_IS_USER (user));
436+        g_return_if_fail (pwent != NULL);
437+
438+        g_object_freeze_notify (G_OBJECT (user));
439+
440+        /* Display Name */
441+        if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
442+                gchar *first_comma;
443+                gchar *real_name_utf8;
444+
445+                real_name_utf8 = g_locale_to_utf8 (pwent->pw_gecos, -1, NULL, NULL, NULL);
446+
447+                first_comma = strchr (real_name_utf8, ',');
448+                if (first_comma) {
449+                        real_name = g_strndup (real_name_utf8, first_comma - real_name_utf8);
450+                        g_free (real_name_utf8);
451+                } else {
452+                        real_name = real_name_utf8;
453+                }
454+
455+                if (real_name[0] == '\0') {
456+                        g_free (real_name);
457+                        real_name = NULL;
458+                }
459+        } else {
460+                real_name = NULL;
461+        }
462+
463+        if ((real_name && !user->real_name) ||
464+            (!real_name && user->real_name) ||
465+            (real_name &&
466+             user->real_name &&
467+             strcmp (real_name, user->real_name) != 0)) {
468+                g_free (user->real_name);
469+                user->real_name = real_name;
470+                g_object_notify (G_OBJECT (user), "real-name");
471+        } else {
472+                g_free (real_name);
473+        }
474+
475+        /* UID */
476+        if (pwent->pw_uid != user->uid) {
477+                user->uid = pwent->pw_uid;
478+                g_object_notify (G_OBJECT (user), "uid");
479+        }
480+
481+        /* Username */
482+        if ((pwent->pw_name && !user->user_name) ||
483+            (!pwent->pw_name && user->user_name) ||
484+            (pwent->pw_name &&
485+             user->user_name &&
486+             strcmp (user->user_name, pwent->pw_name) != 0)) {
487+                g_free (user->user_name);
488+                user->user_name = g_strdup (pwent->pw_name);
489+                g_object_notify (G_OBJECT (user), "user-name");
490+        }
491+
492+        /* Home Directory */
493+        if ((pwent->pw_dir && !user->home_dir) ||
494+            (!pwent->pw_dir && user->home_dir) ||
495+            strcmp (user->home_dir, pwent->pw_dir) != 0) {
496+                g_free (user->home_dir);
497+                user->home_dir = g_strdup (pwent->pw_dir);
498+                g_object_notify (G_OBJECT (user), "home-directory");
499+                g_signal_emit (user, signals[ICON_CHANGED], 0);
500+        }
501+
502+        /* Shell */
503+        if ((pwent->pw_shell && !user->shell) ||
504+            (!pwent->pw_shell && user->shell) ||
505+            (pwent->pw_shell &&
506+             user->shell &&
507+             strcmp (user->shell, pwent->pw_shell) != 0)) {
508+                g_free (user->shell);
509+                user->shell = g_strdup (pwent->pw_shell);
510+                g_object_notify (G_OBJECT (user), "shell");
511+        }
512+
513+        update_icon_monitor (user);
514+
515+        g_object_thaw_notify (G_OBJECT (user));
516+}
517+
518+/**
519+ * gdm_user_get_uid:
520+ * @user: the user object to examine.
521+ *
522+ * Retrieves the ID of @user.
523+ *
524+ * Returns: a pointer to an array of characters which must not be modified or
525+ *  freed, or %NULL.
526+ *
527+ * Since: 1.0
528+ **/
529+
530+uid_t
531+gdm_user_get_uid (GdmUser *user)
532+{
533+        g_return_val_if_fail (GDM_IS_USER (user), -1);
534+
535+        return user->uid;
536+}
537+
538+/**
539+ * gdm_user_get_real_name:
540+ * @user: the user object to examine.
541+ *
542+ * Retrieves the display name of @user.
543+ *
544+ * Returns: a pointer to an array of characters which must not be modified or
545+ *  freed, or %NULL.
546+ *
547+ * Since: 1.0
548+ **/
549+G_CONST_RETURN gchar *
550+gdm_user_get_real_name (GdmUser *user)
551+{
552+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
553+
554+        return (user->real_name ? user->real_name : user->user_name);
555+}
556+
557+/**
558+ * gdm_user_get_user_name:
559+ * @user: the user object to examine.
560+ *
561+ * Retrieves the login name of @user.
562+ *
563+ * Returns: a pointer to an array of characters which must not be modified or
564+ *  freed, or %NULL.
565+ *
566+ * Since: 1.0
567+ **/
568+
569+G_CONST_RETURN gchar *
570+gdm_user_get_user_name (GdmUser *user)
571+{
572+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
573+
574+        return user->user_name;
575+}
576+
577+/**
578+ * gdm_user_get_home_directory:
579+ * @user: the user object to examine.
580+ *
581+ * Retrieves the home directory of @user.
582+ *
583+ * Returns: a pointer to an array of characters which must not be modified or
584+ *  freed, or %NULL.
585+ *
586+ * Since: 1.0
587+ **/
588+
589+G_CONST_RETURN gchar *
590+gdm_user_get_home_directory (GdmUser *user)
591+{
592+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
593+
594+        return user->home_dir;
595+}
596+
597+/**
598+ * gdm_user_get_shell:
599+ * @user: the user object to examine.
600+ *
601+ * Retrieves the login shell of @user.
602+ *
603+ * Returns: a pointer to an array of characters which must not be modified or
604+ *  freed, or %NULL.
605+ *
606+ * Since: 1.0
607+ **/
608+
609+G_CONST_RETURN gchar *
610+gdm_user_get_shell (GdmUser *user)
611+{
612+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
613+
614+        return user->shell;
615+}
616+
617+gulong
618+gdm_user_get_login_frequency (GdmUser *user)
619+{
620+        g_return_val_if_fail (GDM_IS_USER (user), 0);
621+
622+        return user->login_frequency;
623+}
624+
625+G_CONST_RETURN gchar *
626+gdm_user_get_icon_url (GdmUser *user)
627+{
628+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
629+
630+        /* FIXME: Icon can be one of:
631+         * ~/.face
632+         * ~/.face.icon
633+         * ~/.gnome/gdm:[face]picture
634+         * ${GlobalFaceDir}/${username}
635+         * ${GlobalFaceDir}/${username}.png
636+         * but we only monitor the first.
637+         */   
638+        return user->icon_url;
639+}
640diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/gdm-user.h gdm-2.28.0.new/daemon/gdm-user.h
641--- gdm-2.28.0/daemon/gdm-user.h        1970-01-01 10:00:00.000000000 +1000
642+++ gdm-2.28.0.new/daemon/gdm-user.h    2009-10-14 18:30:51.000000000 +1100
643@@ -0,0 +1,53 @@
644+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
645+ *
646+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
647+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
648+ *
649+ * This program is free software; you can redistribute it and/or modify
650+ * it under the terms of the GNU General Public License as published by
651+ * the Free Software Foundation; either version 2 of the License, or
652+ * (at your option) any later version.
653+ *
654+ * This program is distributed in the hope that it will be useful,
655+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
656+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
657+ * GNU General Public License for more details.
658+ *
659+ * You should have received a copy of the GNU General Public License
660+ * along with this program; if not, write to the Free Software
661+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
662+ */
663+
664+/*
665+ * Facade object for user data, owned by GdmUserManager
666+ */
667+
668+#ifndef __GDM_USER__
669+#define __GDM_USER__ 1
670+
671+#include <sys/types.h>
672+#include <glib-object.h>
673+
674+G_BEGIN_DECLS
675+
676+#define GDM_TYPE_USER (gdm_user_get_type ())
677+#define GDM_USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_USER, GdmUser))
678+#define GDM_IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_USER))
679+
680+typedef struct _GdmUser GdmUser;
681+
682+GType                  gdm_user_get_type            (void) G_GNUC_CONST;
683+
684+uid_t                  gdm_user_get_uid             (GdmUser   *user);
685+G_CONST_RETURN gchar  *gdm_user_get_user_name       (GdmUser   *user);
686+G_CONST_RETURN gchar  *gdm_user_get_real_name       (GdmUser   *user);
687+G_CONST_RETURN gchar  *gdm_user_get_home_directory  (GdmUser   *user);
688+G_CONST_RETURN gchar  *gdm_user_get_shell           (GdmUser   *user);
689+guint                  gdm_user_get_num_sessions    (GdmUser   *user);
690+GList                 *gdm_user_get_sessions        (GdmUser   *user);
691+gulong                 gdm_user_get_login_frequency (GdmUser   *user);
692+G_CONST_RETURN gchar  *gdm_user_get_icon_url        (GdmUser   *user);
693+
694+G_END_DECLS
695+
696+#endif
697diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/gdm-user-manager.c gdm-2.28.0.new/daemon/gdm-user-manager.c
698--- gdm-2.28.0/daemon/gdm-user-manager.c        1970-01-01 10:00:00.000000000 +1000
699+++ gdm-2.28.0.new/daemon/gdm-user-manager.c    2009-10-14 18:31:03.000000000 +1100
700@@ -0,0 +1,1538 @@
701+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
702+ *
703+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
704+ *
705+ * This program is free software; you can redistribute it and/or modify
706+ * it under the terms of the GNU General Public License as published by
707+ * the Free Software Foundation; either version 2 of the License, or
708+ * (at your option) any later version.
709+ *
710+ * This program is distributed in the hope that it will be useful,
711+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
712+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
713+ * GNU General Public License for more details.
714+ *
715+ * You should have received a copy of the GNU General Public License
716+ * along with this program; if not, write to the Free Software
717+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
718+ *
719+ */
720+
721+#include "config.h"
722+
723+#include <stdlib.h>
724+#include <stdio.h>
725+#include <fcntl.h>
726+#include <unistd.h>
727+#include <string.h>
728+#include <signal.h>
729+#include <errno.h>
730+#include <sys/stat.h>
731+#include <sys/types.h>
732+
733+#ifdef HAVE_PATHS_H
734+#include <paths.h>
735+#endif /* HAVE_PATHS_H */
736+
737+#include <glib.h>
738+#include <glib/gi18n.h>
739+#include <glib/gstdio.h>
740+#include <glib-object.h>
741+#include <gio/gio.h>
742+
743+#include <dbus/dbus.h>
744+#include <dbus/dbus-glib.h>
745+#include <dbus/dbus-glib-lowlevel.h>
746+
747+#include "gdm-user-manager.h"
748+#include "gdm-user-manager-glue.h"
749+#include "gdm-user-private.h"
750+
751+#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
752+
753+#define GDM_DBUS_PATH         "/org/gnome/DisplayManager"
754+#define GDM_USER_MANAGER_DBUS_PATH GDM_DBUS_PATH "/UserManager"
755+#define GDM_USER_MANAGER_DBUS_NAME "org.gnome.DisplayManager.UserManager"
756+
757+#define CK_NAME      "org.freedesktop.ConsoleKit"
758+#define CK_PATH      "/org/freedesktop/ConsoleKit"
759+#define CK_INTERFACE "org.freedesktop.ConsoleKit"
760+
761+#define CK_MANAGER_PATH      "/org/freedesktop/ConsoleKit/Manager"
762+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
763+#define CK_SEAT_INTERFACE    "org.freedesktop.ConsoleKit.Seat"
764+#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
765+
766+#ifdef __sun
767+#define DEFAULT_MINIMAL_UID     100
768+#else
769+#define DEFAULT_MINIMAL_UID     500
770+#endif
771+
772+#ifndef _PATH_SHELLS
773+#define _PATH_SHELLS    "/etc/shells"
774+#endif
775+#define PATH_PASSWD     "/etc/passwd"
776+
777+#define LOGIN_CACHE_FILE CACHEDIR "/login_frequency.cache"
778+
779+#define DEFAULT_EXCLUDE         { "bin",        \
780+                                  "root",       \
781+                                  "daemon",     \
782+                                  "adm",        \
783+                                  "lp",         \
784+                                  "sync",       \
785+                                  "shutdown",   \
786+                                  "halt",       \
787+                                  "mail",       \
788+                                  "news",       \
789+                                  "uucp",       \
790+                                  "operator",   \
791+                                  "nobody",     \
792+                                  "nobody4",    \
793+                                  "noaccess",   \
794+                                  GDM_USERNAME, \
795+                                  "postgres",   \
796+                                  "pvm",        \
797+                                  "rpm",        \
798+                                  "nfsnobody",  \
799+                                  "pcap",       \
800+                                  NULL }
801+
802+struct GdmUserManagerPrivate
803+{
804+        GHashTable            *users;
805+        GHashTable            *sessions;
806+        GHashTable            *exclusions;
807+        GHashTable            *shells;
808+        DBusGConnection       *connection;
809+        DBusGProxy            *seat_proxy;
810+        char                  *seat_id;
811+
812+        GFileMonitor          *passwd_monitor;
813+        GFileMonitor          *shells_monitor;
814+
815+        guint                  reload_id;
816+        guint                  ck_history_id;
817+
818+        guint8                 loaded_passwd : 1;   
819+        guint8                 loaded_cache : 1;
820+};
821+
822+enum {
823+        USERS_LOADED,
824+        USER_ADDED,
825+        USER_REMOVED,
826+        USER_UPDATED,
827+        LAST_SIGNAL
828+};
829+
830+static guint signals [LAST_SIGNAL] = { 0, };
831+
832+static void     gdm_user_manager_class_init (GdmUserManagerClass *klass);
833+static void     gdm_user_manager_init       (GdmUserManager      *user_manager);
834+static void     gdm_user_manager_finalize   (GObject             *object);
835+
836+G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
837+
838+GQuark
839+gdm_user_manager_error_quark (void)
840+{
841+        static GQuark ret = 0;
842+        if (ret == 0) {
843+                ret = g_quark_from_static_string ("gdm_user_manager_error");
844+        }
845+
846+        return ret;
847+}
848+
849+static void
850+on_user_sessions_changed (GdmUser        *user,
851+                          GdmUserManager *manager)
852+{
853+        guint nsessions;
854+
855+        nsessions = gdm_user_get_num_sessions (user);
856+
857+        g_debug ("GdmUserManager: sessions changed user=%s num=%d",
858+                 gdm_user_get_user_name (user),
859+                 nsessions);
860+
861+        /* only signal on zero and one */
862+        if (nsessions > 1) {
863+                return;
864+        }
865+
866+        g_signal_emit (manager, signals [USER_UPDATED], 0, gdm_user_get_uid (user));
867+}
868+
869+static void
870+on_user_icon_changed (GdmUser        *user,
871+                      GdmUserManager *manager)
872+{
873+        g_debug ("GdmUserManager: user icon changed");
874+}
875+
876+static char *
877+get_seat_id_for_session (DBusGConnection *connection,
878+                         const char      *session_id)
879+{
880+        DBusGProxy      *proxy;
881+        GError          *error;
882+        char            *seat_id;
883+        gboolean         res;
884+
885+        proxy = NULL;
886+        seat_id = NULL;
887+
888+        proxy = dbus_g_proxy_new_for_name (connection,
889+                                           CK_NAME,
890+                                           session_id,
891+                                           CK_SESSION_INTERFACE);
892+        if (proxy == NULL) {
893+                g_warning ("Failed to connect to the ConsoleKit session object");
894+                goto out;
895+        }
896+
897+        error = NULL;
898+        res = dbus_g_proxy_call (proxy,
899+                                 "GetSeatId",
900+                                 &error,
901+                                 G_TYPE_INVALID,
902+                                 DBUS_TYPE_G_OBJECT_PATH, &seat_id,
903+                                 G_TYPE_INVALID);
904+        if (! res) {
905+                if (error != NULL) {
906+                        g_debug ("Failed to identify the current seat: %s", error->message);
907+                        g_error_free (error);
908+                } else {
909+                        g_debug ("Failed to identify the current seat");
910+                }
911+        }
912+ out:
913+        if (proxy != NULL) {
914+                g_object_unref (proxy);
915+        }
916+
917+        return seat_id;
918+}
919+
920+static char *
921+get_x11_display_for_session (DBusGConnection *connection,
922+                             const char      *session_id)
923+{
924+        DBusGProxy      *proxy;
925+        GError          *error;
926+        char            *x11_display;
927+        gboolean         res;
928+
929+        proxy = NULL;
930+        x11_display = NULL;
931+
932+        proxy = dbus_g_proxy_new_for_name (connection,
933+                                           CK_NAME,
934+                                           session_id,
935+                                           CK_SESSION_INTERFACE);
936+        if (proxy == NULL) {
937+                g_warning ("Failed to connect to the ConsoleKit session object");
938+                goto out;
939+        }
940+
941+        error = NULL;
942+        res = dbus_g_proxy_call (proxy,
943+                                 "GetX11Display",
944+                                 &error,
945+                                 G_TYPE_INVALID,
946+                                 G_TYPE_STRING, &x11_display,
947+                                 G_TYPE_INVALID);
948+        if (! res) {
949+                if (error != NULL) {
950+                        g_debug ("Failed to identify the x11 display: %s", error->message);
951+                        g_error_free (error);
952+                } else {
953+                        g_debug ("Failed to identify the x11 display");
954+                }
955+        }
956+ out:
957+        if (proxy != NULL) {
958+                g_object_unref (proxy);
959+        }
960+
961+        return x11_display;
962+}
963+
964+static gboolean
965+maybe_add_session_for_user (GdmUserManager *manager,
966+                            GdmUser        *user,
967+                            const char     *ssid)
968+{
969+        char    *sid;
970+        char    *x11_display;
971+        gboolean ret;
972+
973+        ret = FALSE;
974+        sid = NULL;
975+        x11_display = NULL;
976+
977+        /* skip if on another seat */
978+        sid = get_seat_id_for_session (manager->priv->connection, ssid);
979+        if (sid == NULL
980+            || manager->priv->seat_id == NULL
981+            || strcmp (sid, manager->priv->seat_id) != 0) {
982+                g_debug ("GdmUserManager: not adding session on other seat: %s", ssid);
983+                goto out;
984+        }
985+
986+        /* skip if doesn't have an x11 display */
987+        x11_display = get_x11_display_for_session (manager->priv->connection, ssid);
988+        if (x11_display == NULL || x11_display[0] == '\0') {
989+                g_debug ("GdmUserManager: not adding session without a x11 display: %s", ssid);
990+                goto out;
991+        }
992+
993+        if (g_hash_table_lookup (manager->priv->exclusions, gdm_user_get_user_name (user))) {
994+                g_debug ("GdmUserManager: excluding user '%s'", gdm_user_get_user_name (user));
995+                goto out;
996+        }
997+
998+        g_hash_table_insert (manager->priv->sessions,
999+                             g_strdup (ssid),
1000+                             g_strdup (gdm_user_get_user_name (user)));
1001+
1002+        _gdm_user_add_session (user, ssid);
1003+        g_debug ("GdmUserManager: added session for user: %s", gdm_user_get_user_name (user));
1004+
1005+        ret = TRUE;
1006+
1007+ out:
1008+        g_free (sid);
1009+        g_free (x11_display);
1010+
1011+        return ret;
1012+}
1013+
1014+static void
1015+add_sessions_for_user (GdmUserManager *manager,
1016+                       GdmUser        *user)
1017+{
1018+        DBusGProxy      *proxy;
1019+        GError          *error;
1020+        gboolean         res;
1021+        guint32          uid;
1022+        GPtrArray       *sessions;
1023+        int              i;
1024+
1025+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
1026+                                           CK_NAME,
1027+                                           CK_MANAGER_PATH,
1028+                                           CK_MANAGER_INTERFACE);
1029+        if (proxy == NULL) {
1030+                g_warning ("Failed to connect to the ConsoleKit manager object");
1031+                goto out;
1032+        }
1033+
1034+        uid = gdm_user_get_uid (user);
1035+
1036+        g_debug ("Getting list of sessions for user %u", uid);
1037+
1038+        error = NULL;
1039+        res = dbus_g_proxy_call (proxy,
1040+                                 "GetSessionsForUnixUser",
1041+                                 &error,
1042+                                 G_TYPE_UINT, uid,
1043+                                 G_TYPE_INVALID,
1044+                                 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
1045+                                 &sessions,
1046+                                 G_TYPE_INVALID);
1047+        if (! res) {
1048+                if (error != NULL) {
1049+                        g_debug ("Failed to find sessions for user: %s", error->message);
1050+                        g_error_free (error);
1051+                } else {
1052+                        g_debug ("Failed to find sessions for user");
1053+                }
1054+                goto out;
1055+        }
1056+
1057+        g_debug ("Found %d sessions for user %s", sessions->len, gdm_user_get_user_name (user));
1058+
1059+        for (i = 0; i < sessions->len; i++) {
1060+                char *ssid;
1061+
1062+                ssid = g_ptr_array_index (sessions, i);
1063+                maybe_add_session_for_user (manager, user, ssid);
1064+        }
1065+
1066+        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
1067+        g_ptr_array_free (sessions, TRUE);
1068+
1069+ out:
1070+        if (proxy != NULL) {
1071+                g_object_unref (proxy);
1072+        }
1073+}
1074+
1075+static GdmUser *
1076+create_user (GdmUserManager *manager)
1077+{
1078+        GdmUser *user;
1079+
1080+        user = g_object_new (GDM_TYPE_USER, NULL);
1081+        g_signal_connect (user,
1082+                          "sessions-changed",
1083+                          G_CALLBACK (on_user_sessions_changed),
1084+                          manager);
1085+        g_signal_connect (user,
1086+                          "icon-changed",
1087+                          G_CALLBACK (on_user_icon_changed),
1088+                          manager);
1089+        return user;
1090+}
1091+
1092+static void
1093+add_user (GdmUserManager *manager,
1094+          GdmUser        *user)
1095+{
1096+        add_sessions_for_user (manager, user);
1097+        g_hash_table_insert (manager->priv->users,
1098+                             g_strdup (gdm_user_get_user_name (user)),
1099+                             g_object_ref (user));
1100+
1101+        g_signal_emit (manager, signals[USER_ADDED], 0, gdm_user_get_uid (user));
1102+}
1103+
1104+static GdmUser *
1105+add_new_user_for_pwent (GdmUserManager *manager,
1106+                        struct passwd  *pwent)
1107+{
1108+        GdmUser *user;
1109+
1110+        g_debug ("Creating new user");
1111+
1112+        user = create_user (manager);
1113+        _gdm_user_update (user, pwent);
1114+
1115+        add_user (manager, user);
1116+
1117+        return user;
1118+}
1119+
1120+static char *
1121+get_current_seat_id (DBusGConnection *connection)
1122+{
1123+        DBusGProxy      *proxy;
1124+        GError          *error;
1125+        char            *session_id;
1126+        char            *seat_id;
1127+        gboolean         res;
1128+
1129+        proxy = NULL;
1130+        session_id = NULL;
1131+        seat_id = NULL;
1132+
1133+        proxy = dbus_g_proxy_new_for_name (connection,
1134+                                           CK_NAME,
1135+                                           CK_MANAGER_PATH,
1136+                                           CK_MANAGER_INTERFACE);
1137+        if (proxy == NULL) {
1138+                g_warning ("Failed to connect to the ConsoleKit manager object");
1139+                goto out;
1140+        }
1141+
1142+        error = NULL;
1143+        res = dbus_g_proxy_call (proxy,
1144+                                 "GetCurrentSession",
1145+                                 &error,
1146+                                 G_TYPE_INVALID,
1147+                                 DBUS_TYPE_G_OBJECT_PATH,
1148+                                 &session_id,
1149+                                 G_TYPE_INVALID);
1150+        if (! res) {
1151+                if (error != NULL) {
1152+                        g_debug ("Failed to identify the current session: %s", error->message);
1153+                        g_error_free (error);
1154+                } else {
1155+                        g_debug ("Failed to identify the current session");
1156+                }
1157+                goto out;
1158+        }
1159+
1160+        seat_id = get_seat_id_for_session (connection, session_id);
1161+
1162+ out:
1163+        if (proxy != NULL) {
1164+                g_object_unref (proxy);
1165+        }
1166+        g_free (session_id);
1167+
1168+        return seat_id;
1169+}
1170+
1171+static gboolean
1172+get_uid_from_session_id (GdmUserManager *manager,
1173+                         const char     *session_id,
1174+                         uid_t          *uidp)
1175+{
1176+        DBusGProxy      *proxy;
1177+        GError          *error;
1178+        guint            uid;
1179+        gboolean         res;
1180+
1181+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
1182+                                           CK_NAME,
1183+                                           session_id,
1184+                                           CK_SESSION_INTERFACE);
1185+        if (proxy == NULL) {
1186+                g_warning ("Failed to connect to the ConsoleKit session object");
1187+                return FALSE;
1188+        }
1189+
1190+        error = NULL;
1191+        res = dbus_g_proxy_call (proxy,
1192+                                 "GetUnixUser",
1193+                                 &error,
1194+                                 G_TYPE_INVALID,
1195+                                 G_TYPE_UINT, &uid,
1196+                                 G_TYPE_INVALID);
1197+        g_object_unref (proxy);
1198+
1199+        if (! res) {
1200+                if (error != NULL) {
1201+                        g_warning ("Failed to query the session: %s", error->message);
1202+                        g_error_free (error);
1203+                } else {
1204+                        g_warning ("Failed to query the session");
1205+                }
1206+                return FALSE;
1207+        }
1208+
1209+        if (uidp != NULL) {
1210+                *uidp = (uid_t) uid;
1211+        }
1212+
1213+        return TRUE;
1214+}
1215+
1216+static void
1217+seat_session_added (DBusGProxy     *seat_proxy,
1218+                    const char     *session_id,
1219+                    GdmUserManager *manager)
1220+{
1221+        uid_t          uid;
1222+        gboolean       res;
1223+        struct passwd *pwent;
1224+        GdmUser       *user;
1225+        gboolean       is_new;
1226+
1227+        g_debug ("Session added: %s", session_id);
1228+
1229+        res = get_uid_from_session_id (manager, session_id, &uid);
1230+        if (! res) {
1231+                g_warning ("Unable to lookup user for session");
1232+                return;
1233+        }
1234+
1235+        errno = 0;
1236+        pwent = getpwuid (uid);
1237+        if (pwent == NULL) {
1238+                g_warning ("Unable to lookup user id %d: %s", (int)uid, g_strerror (errno));
1239+                return;
1240+        }
1241+
1242+        /* check exclusions up front */
1243+        if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
1244+                g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
1245+                return;
1246+        }
1247+
1248+        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
1249+        if (user == NULL) {
1250+                g_debug ("Creating new user");
1251+
1252+                user = create_user (manager);
1253+                _gdm_user_update (user, pwent);
1254+                is_new = TRUE;
1255+        } else {
1256+                is_new = FALSE;
1257+        }
1258+
1259+        res = maybe_add_session_for_user (manager, user, session_id);
1260+
1261+        /* only add the user if we added a session */
1262+        if (is_new) {
1263+                if (res) {
1264+                        add_user (manager, user);
1265+                } else {
1266+                        g_object_unref (user);
1267+                }
1268+        }
1269+}
1270+
1271+static void
1272+seat_session_removed (DBusGProxy     *seat_proxy,
1273+                      const char     *session_id,
1274+                      GdmUserManager *manager)
1275+{
1276+        GdmUser *user;
1277+        char    *username;
1278+
1279+        g_debug ("Session removed: %s", session_id);
1280+
1281+        /* since the session object may already be gone
1282+         * we can't query CK directly */
1283+
1284+        username = g_hash_table_lookup (manager->priv->sessions, session_id);
1285+        if (username == NULL) {
1286+                return;
1287+        }
1288+
1289+        user = g_hash_table_lookup (manager->priv->users, username);
1290+        if (user == NULL) {
1291+                /* nothing to do */
1292+                return;
1293+        }
1294+
1295+        g_debug ("GdmUserManager: Session removed for %s", username);
1296+        _gdm_user_remove_session (user, session_id);
1297+}
1298+
1299+static void
1300+on_proxy_destroy (DBusGProxy     *proxy,
1301+                  GdmUserManager *manager)
1302+{
1303+        g_debug ("GdmUserManager: seat proxy destroyed");
1304+
1305+        manager->priv->seat_proxy = NULL;
1306+}
1307+
1308+static void
1309+get_seat_proxy (GdmUserManager *manager)
1310+{
1311+        DBusGProxy      *proxy;
1312+        GError          *error;
1313+
1314+        g_assert (manager->priv->seat_proxy == NULL);
1315+
1316+        error = NULL;
1317+        manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
1318+        if (manager->priv->connection == NULL) {
1319+                if (error != NULL) {
1320+                        g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
1321+                        g_error_free (error);
1322+                } else {
1323+                        g_warning ("Failed to connect to the D-Bus daemon");
1324+                }
1325+                return;
1326+        }
1327+
1328+        manager->priv->seat_id = get_current_seat_id (manager->priv->connection);
1329+        if (manager->priv->seat_id == NULL) {
1330+                return;
1331+        }
1332+
1333+        g_debug ("GdmUserManager: Found current seat: %s", manager->priv->seat_id);
1334+
1335+        error = NULL;
1336+        proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
1337+                                                 CK_NAME,
1338+                                                 manager->priv->seat_id,
1339+                                                 CK_SEAT_INTERFACE,
1340+                                                 &error);
1341+
1342+        if (proxy == NULL) {
1343+                if (error != NULL) {
1344+                        g_warning ("Failed to connect to the ConsoleKit seat object: %s",
1345+                                   error->message);
1346+                        g_error_free (error);
1347+                } else {
1348+                        g_warning ("Failed to connect to the ConsoleKit seat object");
1349+                }
1350+                return;
1351+        }
1352+
1353+        g_signal_connect (proxy, "destroy", G_CALLBACK (on_proxy_destroy), manager);
1354+
1355+        dbus_g_proxy_add_signal (proxy,
1356+                                 "SessionAdded",
1357+                                 DBUS_TYPE_G_OBJECT_PATH,
1358+                                 G_TYPE_INVALID);
1359+        dbus_g_proxy_add_signal (proxy,
1360+                                 "SessionRemoved",
1361+                                 DBUS_TYPE_G_OBJECT_PATH,
1362+                                 G_TYPE_INVALID);
1363+        dbus_g_proxy_connect_signal (proxy,
1364+                                     "SessionAdded",
1365+                                     G_CALLBACK (seat_session_added),
1366+                                     manager,
1367+                                     NULL);
1368+        dbus_g_proxy_connect_signal (proxy,
1369+                                     "SessionRemoved",
1370+                                     G_CALLBACK (seat_session_removed),
1371+                                     manager,
1372+                                     NULL);
1373+        manager->priv->seat_proxy = proxy;
1374+
1375+}
1376+
1377+/**
1378+ * gdm_manager_get_user:
1379+ * @manager: the manager to query.
1380+ * @username: the login name of the user to get.
1381+ *
1382+ * Retrieves a pointer to the #GdmUser object for the login named @username
1383+ * from @manager. This pointer is not a reference, and should not be released.
1384+ *
1385+ * Returns: a pointer to a #GdmUser object.
1386+ **/
1387+static GdmUser *
1388+gdm_user_manager_get_user (GdmUserManager *manager,
1389+                           const char     *username)
1390+{
1391+        GdmUser *user;
1392+
1393+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
1394+        g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
1395+
1396+        user = g_hash_table_lookup (manager->priv->users, username);
1397+
1398+        if (user == NULL) {
1399+                struct passwd *pwent;
1400+
1401+                pwent = getpwnam (username);
1402+
1403+                if (pwent != NULL) {
1404+                        user = add_new_user_for_pwent (manager, pwent);
1405+                }
1406+        }
1407+
1408+        return user;
1409+}
1410+
1411+static GdmUser *
1412+gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
1413+                                  uid_t           uid)
1414+{
1415+        GdmUser       *user;
1416+        struct passwd *pwent;
1417+
1418+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
1419+
1420+        pwent = getpwuid (uid);
1421+        if (pwent == NULL) {
1422+                g_warning ("GdmUserManager: unable to lookup uid %d", (int)uid);
1423+                return NULL;
1424+        }
1425+
1426+        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
1427+
1428+        if (user == NULL) {
1429+                user = add_new_user_for_pwent (manager, pwent);
1430+        }
1431+
1432+        return user;
1433+}
1434+
1435+static void
1436+listify_hash_values_hfunc (gpointer key,
1437+                           gpointer value,
1438+                           gpointer user_data)
1439+{
1440+        GSList **list = user_data;
1441+
1442+        *list = g_slist_prepend (*list, value);
1443+}
1444+
1445+static gboolean
1446+parse_value_as_ulong (const char *value,
1447+                      gulong     *ulongval)
1448+{
1449+        char  *end_of_valid_long;
1450+        glong  long_value;
1451+        gulong ulong_value;
1452+
1453+        errno = 0;
1454+        long_value = strtol (value, &end_of_valid_long, 10);
1455+
1456+        if (*value == '\0' || *end_of_valid_long != '\0') {
1457+                return FALSE;
1458+        }
1459+
1460+        ulong_value = long_value;
1461+        if (ulong_value != long_value || errno == ERANGE) {
1462+                return FALSE;
1463+        }
1464+
1465+        *ulongval = ulong_value;
1466+
1467+        return TRUE;
1468+}
1469+
1470+static gboolean
1471+parse_ck_history_line (const char *line,
1472+                       char      **user_namep,
1473+                       gulong     *frequencyp)
1474+{
1475+        GRegex     *re;
1476+        GMatchInfo *match_info;
1477+        gboolean    res;
1478+        gboolean    ret;
1479+        GError     *error;
1480+
1481+        ret = FALSE;
1482+        re = NULL;
1483+        match_info = NULL;
1484+
1485+        error = NULL;
1486+        re = g_regex_new ("(?P<username>[0-9a-zA-Z]+)[ ]+(?P<frequency>[0-9]+)", 0, 0, &error);
1487+        if (re == NULL) {
1488+                if (error != NULL) {
1489+                        g_critical ("%s", error->message);
1490+                } else {
1491+                        g_critical ("Error in regex call");
1492+                }
1493+                goto out;
1494+        }
1495+
1496+        g_regex_match (re, line, 0, &match_info);
1497+
1498+        res = g_match_info_matches (match_info);
1499+        if (! res) {
1500+                g_warning ("Unable to parse history: %s", line);
1501+                goto out;
1502+        }
1503+
1504+        if (user_namep != NULL) {
1505+                *user_namep = g_match_info_fetch_named (match_info, "username");
1506+        }
1507+
1508+        if (frequencyp != NULL) {
1509+                char *freq;
1510+                freq = g_match_info_fetch_named (match_info, "frequency");
1511+                res = parse_value_as_ulong (freq, frequencyp);
1512+                g_free (freq);
1513+                if (! res) {
1514+                        goto out;
1515+                }
1516+        }
1517+
1518+        ret = TRUE;
1519+
1520+ out:
1521+        if (match_info != NULL) {
1522+                g_match_info_free (match_info);
1523+        }
1524+        if (re != NULL) {
1525+                g_regex_unref (re);
1526+        }
1527+        return ret;
1528+}
1529+
1530+static void
1531+process_ck_history_line (GdmUserManager *manager,
1532+                         const char     *line)
1533+{
1534+        gboolean res;
1535+        char    *username;
1536+        gulong   frequency;
1537+        struct passwd *pwent;
1538+        GdmUser *user;
1539+
1540+        frequency = 0;
1541+        username = NULL;
1542+        res = parse_ck_history_line (line, &username, &frequency);
1543+        if (! res) {
1544+                return;
1545+        }
1546+
1547+        if (g_hash_table_lookup (manager->priv->exclusions, username)) {
1548+                g_debug ("GdmUserManager: excluding user '%s'", username);
1549+                g_free (username);
1550+                return;
1551+        }
1552+
1553+        /* do not show system users; we cannot use gdm_user_manager_get_user()
1554+         * here since this creates/signals users as a side effect */
1555+        pwent = getpwnam (username);
1556+        if (pwent == NULL) {
1557+                g_warning ("Unable to lookup user name %s: %s", username, g_strerror (errno));
1558+                return;
1559+        }
1560+        if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
1561+                g_debug ("GdmUserManager: excluding user '%s'", username);
1562+                return;
1563+        }
1564+
1565+        user = gdm_user_manager_get_user (manager, username);
1566+        if (user == NULL) {
1567+                g_debug ("GdmUserManager: unable to lookup user '%s'", username);
1568+                g_free (username);
1569+                return;
1570+        }
1571+
1572+        g_object_set (user, "login-frequency", frequency, NULL);
1573+        g_signal_emit (manager, signals [USER_UPDATED], 0, gdm_user_get_uid (user));
1574+        g_free (username);
1575+}
1576+
1577+static gboolean
1578+ck_history_watch (GIOChannel     *source,
1579+                  GIOCondition    condition,
1580+                  GdmUserManager *manager)
1581+{
1582+        GIOStatus status;
1583+        gboolean  done  = FALSE;
1584+
1585+        g_return_val_if_fail (manager != NULL, FALSE);
1586+
1587+        if (condition & G_IO_IN) {
1588+                char   *str;
1589+                GError *error;
1590+
1591+                error = NULL;
1592+                status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
1593+                if (error != NULL) {
1594+                        g_warning ("GdmUserManager: unable to read line: %s", error->message);
1595+                        g_error_free (error);
1596+                }
1597+
1598+                if (status == G_IO_STATUS_NORMAL) {
1599+                        g_debug ("GdmUserManager: history output: %s", str);
1600+                        process_ck_history_line (manager, str);
1601+                } else if (status == G_IO_STATUS_EOF) {
1602+                        done = TRUE;
1603+                }
1604+
1605+                g_free (str);
1606+        } else if (condition & G_IO_HUP) {
1607+                done = TRUE;
1608+        }
1609+
1610+        if (done) {
1611+                FILE *fp;
1612+
1613+                /* Cache login counts */
1614+                fp = fopen (LOGIN_CACHE_FILE, "w");
1615+                if (fp != NULL) {
1616+                        GHashTableIter iter;
1617+                        gpointer value;
1618+
1619+                        g_hash_table_iter_init (&iter, manager->priv->users);
1620+                        while (g_hash_table_iter_next (&iter, NULL, &value)) {
1621+                                GdmUser *user = (GdmUser *) value;
1622+                                fprintf (fp, "%s %lu\n",
1623+                                         gdm_user_get_user_name (user),
1624+                                         gdm_user_get_login_frequency (user));
1625+                        }
1626+                        fclose (fp);
1627+                }
1628+                else
1629+                        g_warning ("Unable to write to login cache file: %s", LOGIN_CACHE_FILE);
1630+
1631+                manager->priv->ck_history_id = 0;
1632+                return FALSE;
1633+        }
1634+
1635+        return TRUE;
1636+}
1637+
1638+static void
1639+reload_ck_history (GdmUserManager *manager)
1640+{
1641+        char       *command;
1642+        const char *seat_id;
1643+        GError     *error;
1644+        gboolean    res;
1645+        char      **argv;
1646+        int         standard_out;
1647+        GIOChannel *channel;
1648+
1649+        seat_id = NULL;
1650+        if (manager->priv->seat_id != NULL
1651+            && g_str_has_prefix (manager->priv->seat_id, "/org/freedesktop/ConsoleKit/")) {
1652+
1653+                seat_id = manager->priv->seat_id + strlen ("/org/freedesktop/ConsoleKit/");
1654+        }
1655+
1656+        if (seat_id == NULL) {
1657+                g_warning ("Unable to find users: no seat-id found");
1658+                return;
1659+        }
1660+
1661+        command = g_strdup_printf ("ck-history --frequent --seat='%s' --session-type=''",
1662+                                   seat_id);
1663+        g_debug ("GdmUserManager: running '%s'", command);
1664+        error = NULL;
1665+        if (! g_shell_parse_argv (command, NULL, &argv, &error)) {
1666+                if (error != NULL) {
1667+                        g_warning ("Could not parse command: %s", error->message);
1668+                        g_error_free (error);
1669+                } else {
1670+                        g_warning ("Could not parse command");
1671+                }
1672+                goto out;
1673+        }
1674+
1675+        error = NULL;
1676+        res = g_spawn_async_with_pipes (NULL,
1677+                                        argv,
1678+                                        NULL,
1679+                                        G_SPAWN_SEARCH_PATH,
1680+                                        NULL,
1681+                                        NULL,
1682+                                        NULL, /* pid */
1683+                                        NULL,
1684+                                        &standard_out,
1685+                                        NULL,
1686+                                        &error);
1687+        g_strfreev (argv);
1688+        if (! res) {
1689+                if (error != NULL) {
1690+                        g_warning ("Unable to run ck-history: %s", error->message);
1691+                        g_error_free (error);
1692+                } else {
1693+                        g_warning ("Unable to run ck-history");
1694+                }
1695+                goto out;
1696+        }
1697+
1698+        channel = g_io_channel_unix_new (standard_out);
1699+        g_io_channel_set_close_on_unref (channel, TRUE);
1700+        g_io_channel_set_flags (channel,
1701+                                g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
1702+                                NULL);
1703+        manager->priv->ck_history_id = g_io_add_watch (channel,
1704+                                                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
1705+                                                       (GIOFunc)ck_history_watch,
1706+                                                       manager);
1707+        g_io_channel_unref (channel);
1708+
1709+ out:
1710+        g_free (command);
1711+}
1712+
1713+static void
1714+reload_passwd (GdmUserManager *manager)
1715+{
1716+        struct passwd *pwent;
1717+        GSList        *old_users;
1718+        GSList        *new_users;
1719+        GSList        *list;
1720+        FILE          *fp;
1721+
1722+        old_users = NULL;
1723+        new_users = NULL;
1724+
1725+        errno = 0;
1726+        fp = fopen (PATH_PASSWD, "r");
1727+        if (fp == NULL) {
1728+                g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
1729+                goto out;
1730+        }
1731+
1732+        g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &old_users);
1733+        g_slist_foreach (old_users, (GFunc) g_object_ref, NULL);
1734+
1735+        /* Make sure we keep users who are logged in no matter what. */
1736+        for (list = old_users; list; list = list->next) {
1737+                if (gdm_user_get_num_sessions (list->data) > 0) {
1738+                        g_object_freeze_notify (G_OBJECT (list->data));
1739+                        new_users = g_slist_prepend (new_users, g_object_ref (list->data));
1740+                }
1741+        }
1742+
1743+        for (pwent = fgetpwent (fp); pwent != NULL; pwent = fgetpwent (fp)) {
1744+                GdmUser *user;
1745+
1746+                user = NULL;
1747+
1748+                /* Skip users below MinimalUID... */
1749+                if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
1750+                        continue;
1751+                }
1752+
1753+                /* ...And users w/ invalid shells... */
1754+                if (pwent->pw_shell == NULL ||
1755+                    !g_hash_table_lookup (manager->priv->shells, pwent->pw_shell)) {
1756+                        g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
1757+                        continue;
1758+                }
1759+
1760+                /* ...And explicitly excluded users */
1761+                if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
1762+                        g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
1763+                        continue;
1764+                }
1765+
1766+                user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
1767+
1768+                /* Update users already in the *new* list */
1769+                if (g_slist_find (new_users, user)) {
1770+                        _gdm_user_update (user, pwent);
1771+                        continue;
1772+                }
1773+
1774+                if (user == NULL) {
1775+                        user = create_user (manager);
1776+                } else {
1777+                        g_object_ref (user);
1778+                }
1779+
1780+                /* Freeze & update users not already in the new list */
1781+                g_object_freeze_notify (G_OBJECT (user));
1782+                _gdm_user_update (user, pwent);
1783+
1784+                new_users = g_slist_prepend (new_users, user);
1785+        }
1786+
1787+        /* Go through and handle removed users */
1788+        for (list = old_users; list; list = list->next) {
1789+                if (! g_slist_find (new_users, list->data)) {
1790+                        g_signal_emit (manager, signals[USER_REMOVED], 0, gdm_user_get_uid (GDM_USER (list->data)));
1791+                        g_hash_table_remove (manager->priv->users,
1792+                                             gdm_user_get_user_name (list->data));
1793+                }
1794+        }
1795+
1796+        /* Go through and handle added users */
1797+        for (list = new_users; list; list = list->next) {
1798+                if (!g_slist_find (old_users, list->data)) {
1799+                        add_user (manager, list->data);
1800+                }
1801+        }
1802+
1803+        if (!manager->priv->loaded_passwd) {
1804+                g_signal_emit (manager, signals[USERS_LOADED], 0);
1805+                manager->priv->loaded_passwd = TRUE;
1806+        }
1807+
1808+ out:
1809+        /* Cleanup */
1810+
1811+        fclose (fp);
1812+
1813+        g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL);
1814+        g_slist_foreach (new_users, (GFunc) g_object_unref, NULL);
1815+        g_slist_free (new_users);
1816+
1817+        g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
1818+        g_slist_free (old_users);
1819+}
1820+
1821+static void
1822+load_login_frequency_cache (GdmUserManager *manager)
1823+{
1824+        GIOChannel *channel;
1825+        gchar *line;
1826+
1827+        channel = g_io_channel_new_file (LOGIN_CACHE_FILE, "r", NULL);
1828+        if (channel == NULL)
1829+                return;
1830+
1831+        while (g_io_channel_read_line (channel, &line, NULL, NULL, NULL) == G_IO_STATUS_NORMAL) {
1832+                process_ck_history_line (manager, line);
1833+                g_free (line);
1834+        }
1835+   
1836+        g_io_channel_close (channel);
1837+}
1838+
1839+static void
1840+reload_users (GdmUserManager *manager)
1841+{
1842+        reload_passwd (manager);
1843+        if (!manager->priv->loaded_cache) {
1844+                load_login_frequency_cache (manager);
1845+                manager->priv->loaded_cache = TRUE;
1846+        }
1847+        reload_ck_history (manager);
1848+}
1849+
1850+static gboolean
1851+reload_users_timeout (GdmUserManager *manager)
1852+{
1853+        reload_users (manager);
1854+        manager->priv->reload_id = 0;
1855+
1856+        return FALSE;
1857+}
1858+
1859+static void
1860+queue_reload_users (GdmUserManager *manager)
1861+{
1862+        if (manager->priv->reload_id > 0) {
1863+                return;
1864+        }
1865+
1866+        manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, manager);
1867+}
1868+
1869+static void
1870+reload_shells (GdmUserManager *manager)
1871+{
1872+        char *shell;
1873+
1874+        setusershell ();
1875+
1876+        g_hash_table_remove_all (manager->priv->shells);
1877+        for (shell = getusershell (); shell != NULL; shell = getusershell ()) {
1878+                /* skip well known not-real shells */
1879+                if (shell == NULL
1880+                    || strcmp (shell, "/sbin/nologin") == 0
1881+                    || strcmp (shell, "/bin/false") == 0) {
1882+                        g_debug ("GdmUserManager: skipping shell %s", shell);
1883+                        continue;
1884+                }
1885+                g_hash_table_insert (manager->priv->shells,
1886+                                     g_strdup (shell),
1887+                                     GUINT_TO_POINTER (TRUE));
1888+        }
1889+
1890+        endusershell ();
1891+}
1892+
1893+static void
1894+on_shells_monitor_changed (GFileMonitor     *monitor,
1895+                           GFile            *file,
1896+                           GFile            *other_file,
1897+                           GFileMonitorEvent event_type,
1898+                           GdmUserManager   *manager)
1899+{
1900+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1901+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
1902+                return;
1903+        }
1904+
1905+        reload_shells (manager);
1906+        reload_passwd (manager);
1907+}
1908+
1909+static void
1910+on_passwd_monitor_changed (GFileMonitor     *monitor,
1911+                           GFile            *file,
1912+                           GFile            *other_file,
1913+                           GFileMonitorEvent event_type,
1914+                           GdmUserManager   *manager)
1915+{
1916+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1917+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
1918+                return;
1919+        }
1920+
1921+        reload_passwd (manager);
1922+}
1923+
1924+static void
1925+gdm_user_manager_class_init (GdmUserManagerClass *klass)
1926+{
1927+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
1928+
1929+        object_class->finalize = gdm_user_manager_finalize;
1930+
1931+        signals [USERS_LOADED] =
1932+                g_signal_new ("users-loaded",
1933+                              G_TYPE_FROM_CLASS (klass),
1934+                              G_SIGNAL_RUN_LAST,
1935+                              G_STRUCT_OFFSET (GdmUserManagerClass, users_loaded),
1936+                              NULL, NULL,
1937+                              g_cclosure_marshal_VOID__OBJECT,
1938+                              G_TYPE_NONE, 0);
1939+        signals [USER_ADDED] =
1940+                g_signal_new ("user-added",
1941+                              G_TYPE_FROM_CLASS (klass),
1942+                              G_SIGNAL_RUN_LAST,
1943+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_added),
1944+                              NULL, NULL,
1945+                              g_cclosure_marshal_VOID__OBJECT,
1946+                              G_TYPE_NONE, 1, G_TYPE_INT64);
1947+        signals [USER_REMOVED] =
1948+                g_signal_new ("user-removed",
1949+                              G_TYPE_FROM_CLASS (klass),
1950+                              G_SIGNAL_RUN_LAST,
1951+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_removed),
1952+                              NULL, NULL,
1953+                              g_cclosure_marshal_VOID__OBJECT,
1954+                              G_TYPE_NONE, 1, G_TYPE_INT64);
1955+        signals [USER_UPDATED] =
1956+                g_signal_new ("user-updated",
1957+                              G_TYPE_FROM_CLASS (klass),
1958+                              G_SIGNAL_RUN_LAST,
1959+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_updated),
1960+                              NULL, NULL,
1961+                              g_cclosure_marshal_VOID__OBJECT,
1962+                              G_TYPE_NONE, 1, G_TYPE_INT64);
1963+
1964+        g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
1965+
1966+        dbus_g_object_type_install_info (GDM_TYPE_USER_MANAGER, &dbus_glib_gdm_user_manager_object_info);
1967+}
1968+
1969+static void
1970+gdm_user_manager_init (GdmUserManager *manager)
1971+{
1972+        int            i;
1973+        GFile         *file;
1974+        GError        *error;
1975+        const char    *exclude_default[] = DEFAULT_EXCLUDE;
1976+
1977+        manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
1978+
1979+        /* sessions */
1980+        manager->priv->sessions = g_hash_table_new_full (g_str_hash,
1981+                                                         g_str_equal,
1982+                                                         g_free,
1983+                                                         g_free);
1984+
1985+        /* exclusions */
1986+        manager->priv->exclusions = g_hash_table_new_full (g_str_hash,
1987+                                                           g_str_equal,
1988+                                                           g_free,
1989+                                                           NULL);
1990+        for (i = 0; exclude_default[i] != NULL; i++) {
1991+                g_hash_table_insert (manager->priv->exclusions,
1992+                                     g_strdup (exclude_default [i]),
1993+                                     GUINT_TO_POINTER (TRUE));
1994+        }
1995+
1996+        /* /etc/shells */
1997+        manager->priv->shells = g_hash_table_new_full (g_str_hash,
1998+                                                       g_str_equal,
1999+                                                       g_free,
2000+                                                       NULL);
2001+        reload_shells (manager);
2002+        file = g_file_new_for_path (_PATH_SHELLS);
2003+        error = NULL;
2004+        manager->priv->shells_monitor = g_file_monitor_file (file,
2005+                                                             G_FILE_MONITOR_NONE,
2006+                                                             NULL,
2007+                                                             &error);
2008+        if (manager->priv->shells_monitor != NULL) {
2009+                g_signal_connect (manager->priv->shells_monitor,
2010+                                  "changed",
2011+                                  G_CALLBACK (on_shells_monitor_changed),
2012+                                  manager);
2013+        } else {
2014+                g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
2015+                g_error_free (error);
2016+        }
2017+        g_object_unref (file);
2018+
2019+        /* /etc/passwd */
2020+        manager->priv->users = g_hash_table_new_full (g_str_hash,
2021+                                                      g_str_equal,
2022+                                                      g_free,
2023+                                                      (GDestroyNotify) g_object_run_dispose);
2024+        file = g_file_new_for_path (PATH_PASSWD);
2025+        manager->priv->passwd_monitor = g_file_monitor_file (file,
2026+                                                             G_FILE_MONITOR_NONE,
2027+                                                             NULL,
2028+                                                             &error);
2029+        if (manager->priv->passwd_monitor != NULL) {
2030+                g_signal_connect (manager->priv->passwd_monitor,
2031+                                  "changed",
2032+                                  G_CALLBACK (on_passwd_monitor_changed),
2033+                                  manager);
2034+        } else {
2035+                g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
2036+                g_error_free (error);
2037+        }
2038+        g_object_unref (file);
2039+
2040+
2041+        get_seat_proxy (manager);
2042+
2043+        queue_reload_users (manager);
2044+
2045+        dbus_g_connection_register_g_object (manager->priv->connection, GDM_USER_MANAGER_DBUS_PATH, G_OBJECT (manager));
2046+}
2047+
2048+static void
2049+gdm_user_manager_finalize (GObject *object)
2050+{
2051+        GdmUserManager *manager;
2052+
2053+        g_return_if_fail (object != NULL);
2054+        g_return_if_fail (GDM_IS_USER_MANAGER (object));
2055+
2056+        manager = GDM_USER_MANAGER (object);
2057+
2058+        g_return_if_fail (manager->priv != NULL);
2059+
2060+        if (manager->priv->seat_proxy != NULL) {
2061+                g_object_unref (manager->priv->seat_proxy);
2062+        }
2063+
2064+        if (manager->priv->ck_history_id != 0) {
2065+                g_source_remove (manager->priv->ck_history_id);
2066+                manager->priv->ck_history_id = 0;
2067+        }
2068+
2069+        if (manager->priv->reload_id > 0) {
2070+                g_source_remove (manager->priv->reload_id);
2071+                manager->priv->reload_id = 0;
2072+        }
2073+
2074+        g_hash_table_destroy (manager->priv->sessions);
2075+
2076+        g_file_monitor_cancel (manager->priv->passwd_monitor);
2077+        g_hash_table_destroy (manager->priv->users);
2078+
2079+        g_file_monitor_cancel (manager->priv->shells_monitor);
2080+        g_hash_table_destroy (manager->priv->shells);
2081+
2082+        g_free (manager->priv->seat_id);
2083+
2084+        G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
2085+}
2086+
2087+GdmUserManager *
2088+gdm_user_manager_new (void)
2089+{
2090+        return GDM_USER_MANAGER (g_object_new (GDM_TYPE_USER_MANAGER, NULL));
2091+}
2092+
2093+/*
2094+  Example:
2095+  dbus-send --system --print-reply --dest=org.gnome.DisplayManager \
2096+  /org/gnome/DisplayManager/UserManager org.gnome.DisplayManager.UserManager.CountUsers
2097+*/
2098+gboolean
2099+gdm_user_manager_count_users (GdmUserManager *user_manager,
2100+                              gint           *user_count,
2101+                              GError        **error)
2102+{
2103+        *user_count = g_hash_table_size (user_manager->priv->users);
2104+   
2105+        return TRUE;
2106+}
2107+
2108+/*
2109+  Example:
2110+  dbus-send --system --print-reply --dest=org.gnome.DisplayManager \
2111+  /org/gnome/DisplayManager/UserManager org.gnome.DisplayManager.UserManager.GetUserList
2112+*/
2113+gboolean
2114+gdm_user_manager_get_user_list (GdmUserManager *user_manager,
2115+                                GArray        **user_list,
2116+                                GError        **error)
2117+{
2118+        GHashTableIter iter;
2119+        GdmUser *user;
2120+
2121+        *user_list = g_array_new (FALSE, FALSE, sizeof (gint64));
2122+        g_hash_table_iter_init (&iter, user_manager->priv->users);
2123+        while (g_hash_table_iter_next (&iter, NULL, (gpointer *)&user)) {
2124+                gint64 uid = gdm_user_get_uid (user);
2125+                g_array_append_val (*user_list, uid);
2126+        }
2127+
2128+        return TRUE;
2129+}
2130+
2131+
2132+/*
2133+  Example:
2134+  dbus-send --system --print-reply --dest=org.gnome.DisplayManager \
2135+  /org/gnome/DisplayManager/UserManager org.gnome.DisplayManager.UserManager.GetUserInfo int64:1000
2136+*/
2137+gboolean
2138+gdm_user_manager_get_user_info (GdmUserManager *user_manager,
2139+                                gint64          uid,
2140+                                gchar         **user_name,
2141+                                gchar         **real_name,
2142+                                gchar         **shell,
2143+                                gint           *login_count,
2144+                                gchar         **icon_url,
2145+                                GError        **error)
2146+{
2147+        GdmUser *user;
2148+
2149+        user = gdm_user_manager_get_user_by_uid (user_manager, uid);
2150+        if (user == NULL)
2151+                return FALSE;
2152+
2153+        *user_name = g_strdup (gdm_user_get_user_name (user));
2154+        *real_name = g_strdup (gdm_user_get_real_name (user));
2155+        *login_count = gdm_user_get_login_frequency (user);
2156+        *shell = g_strdup (gdm_user_get_shell (user));
2157+        *icon_url = g_strdup (gdm_user_get_icon_url (user));
2158+
2159+        return TRUE;
2160+}
2161+
2162+/*
2163+  Example:
2164+  dbus-send --system --print-reply --dest=org.gnome.DisplayManager \
2165+  /org/gnome/DisplayManager/UserManager org.gnome.DisplayManager.UserManager.GetUsersInfo array:int64:1000,1001
2166+*/
2167+gboolean
2168+gdm_user_manager_get_users_info (GdmUserManager *user_manager,
2169+                                 GArray         *uids,
2170+                                 GPtrArray     **users_info,
2171+                                 GError        **error)
2172+{
2173+        int i;
2174+   
2175+        *users_info = g_ptr_array_new ();
2176+
2177+        for (i = 0; i < uids->len; i++) {
2178+                gint64 uid;
2179+                GdmUser *user;
2180+                GValueArray *user_info;
2181+                GValue arg = {0};
2182+
2183+                uid = g_array_index (uids, gint64, i);
2184+                user = gdm_user_manager_get_user_by_uid (user_manager, uid);
2185+                if (user == NULL)
2186+                        continue;
2187+
2188+                user_info = g_value_array_new (5);
2189+
2190+                g_value_init (&arg, G_TYPE_INT64);
2191+                g_value_set_int64 (&arg, uid);
2192+                g_value_array_append (user_info, &arg);
2193+                g_value_unset (&arg);
2194+
2195+                g_value_init (&arg, G_TYPE_STRING);
2196+                g_value_set_string (&arg, gdm_user_get_user_name (user));
2197+                g_value_array_append (user_info, &arg);
2198+                g_value_unset (&arg);
2199+
2200+                g_value_init (&arg, G_TYPE_STRING);
2201+                g_value_set_string (&arg, gdm_user_get_real_name (user));
2202+                g_value_array_append (user_info, &arg);
2203+                g_value_unset (&arg);
2204+
2205+                g_value_init (&arg, G_TYPE_STRING);
2206+                g_value_set_string (&arg, gdm_user_get_shell (user));
2207+                g_value_array_append (user_info, &arg);
2208+                g_value_unset (&arg);
2209+
2210+                g_value_init (&arg, G_TYPE_INT);
2211+                g_value_set_int (&arg, gdm_user_get_login_frequency (user));
2212+                g_value_array_append (user_info, &arg);
2213+                g_value_unset (&arg);
2214+
2215+                g_value_init (&arg, G_TYPE_STRING);
2216+                g_value_set_string (&arg, gdm_user_get_icon_url (user));
2217+                g_value_array_append (user_info, &arg);
2218+                g_value_unset (&arg);
2219+
2220+                g_ptr_array_add (*users_info, user_info);
2221+        }
2222+   
2223+        return TRUE;
2224+}
2225+
2226+/*
2227+  Example:
2228+  dbus-send --system --print-reply --dest=org.gnome.DisplayManager \
2229+  /org/gnome/DisplayManager/UserManager org.gnome.DisplayManager.UserManager.GetUsersLoaded
2230+*/
2231+gboolean
2232+gdm_user_manager_get_users_loaded (GdmUserManager *user_manager,
2233+                                   gboolean       *is_loaded,
2234+                                   GError        **error)
2235+{
2236+        *is_loaded = user_manager->priv->loaded_passwd;
2237+        return TRUE;
2238+}
2239diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/gdm-user-manager.h gdm-2.28.0.new/daemon/gdm-user-manager.h
2240--- gdm-2.28.0/daemon/gdm-user-manager.h        1970-01-01 10:00:00.000000000 +1000
2241+++ gdm-2.28.0.new/daemon/gdm-user-manager.h    2009-10-14 18:30:51.000000000 +1100
2242@@ -0,0 +1,93 @@
2243+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2244+ *
2245+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
2246+ *
2247+ * This program is free software; you can redistribute it and/or modify
2248+ * it under the terms of the GNU General Public License as published by
2249+ * the Free Software Foundation; either version 2 of the License, or
2250+ * (at your option) any later version.
2251+ *
2252+ * This program is distributed in the hope that it will be useful,
2253+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2254+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2255+ * GNU General Public License for more details.
2256+ *
2257+ * You should have received a copy of the GNU General Public License
2258+ * along with this program; if not, write to the Free Software
2259+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2260+ *
2261+ */
2262+
2263+#ifndef __GDM_USER_MANAGER_H
2264+#define __GDM_USER_MANAGER_H
2265+
2266+#include <glib-object.h>
2267+
2268+#include "gdm-user.h"
2269+
2270+G_BEGIN_DECLS
2271+
2272+#define GDM_TYPE_USER_MANAGER         (gdm_user_manager_get_type ())
2273+#define GDM_USER_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_MANAGER, GdmUserManager))
2274+#define GDM_USER_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
2275+#define GDM_IS_USER_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_MANAGER))
2276+#define GDM_IS_USER_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_MANAGER))
2277+#define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
2278+
2279+typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
2280+
2281+typedef struct
2282+{
2283+        GObject                parent;
2284+        GdmUserManagerPrivate *priv;
2285+} GdmUserManager;
2286+
2287+typedef struct
2288+{
2289+        GObjectClass   parent_class;
2290+
2291+        void          (* users_loaded)              (GdmUserManager *user_manager);
2292+        void          (* user_added)                (GdmUserManager *user_manager,
2293+                                                     gint64          uid);
2294+        void          (* user_removed)              (GdmUserManager *user_manager,
2295+                                                     gint64          uid);
2296+        void          (* user_updated)              (GdmUserManager *user_manager,
2297+                                                     gint64          uid);
2298+} GdmUserManagerClass;
2299+
2300+#define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
2301+
2302+GQuark              gdm_user_manager_error_quark           (void);
2303+GType               gdm_user_manager_get_type              (void);
2304+
2305+GdmUserManager *    gdm_user_manager_new                   (void);
2306+
2307+gboolean            gdm_user_manager_count_users           (GdmUserManager *user_manager,
2308+                                                            gint           *user_count,
2309+                                                            GError        **error);
2310+
2311+gboolean            gdm_user_manager_get_user_list         (GdmUserManager *user_manager,
2312+                                                            GArray        **user_list,
2313+                                                            GError        **error);
2314+
2315+gboolean            gdm_user_manager_get_user_info         (GdmUserManager *user_manager,
2316+                                                            gint64          uid,
2317+                                                            gchar         **user_name,
2318+                                                            gchar         **real_name,
2319+                                                            gchar         **shell,
2320+                                                            gint           *login_count,
2321+                                                            gchar         **icon_url,
2322+                                                            GError        **error);
2323+
2324+gboolean            gdm_user_manager_get_users_info        (GdmUserManager *user_manager,
2325+                                                            GArray         *uids,
2326+                                                            GPtrArray     **user_info,
2327+                                                            GError        **error);
2328+
2329+gboolean            gdm_user_manager_get_users_loaded      (GdmUserManager *user_manager,
2330+                                                            gboolean       *is_loaded,
2331+                                                            GError        **error);
2332+
2333+G_END_DECLS
2334+
2335+#endif /* __GDM_USER_MANAGER_H */
2336diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/gdm-user-manager.xml gdm-2.28.0.new/daemon/gdm-user-manager.xml
2337--- gdm-2.28.0/daemon/gdm-user-manager.xml      1970-01-01 10:00:00.000000000 +1000
2338+++ gdm-2.28.0.new/daemon/gdm-user-manager.xml  2009-10-14 18:30:51.000000000 +1100
2339@@ -0,0 +1,56 @@
2340+<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-BUS Object Introspection 1.0//EN" "http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
2341+<node name="/org/gnome/DisplayManager/UserManager">
2342+  <interface name="org.gnome.DisplayManager.UserManager">
2343+
2344+    <!-- Get the number of known users -->
2345+    <method name="CountUsers">
2346+      <arg name="user_count" direction="out" type="i"/>
2347+    </method>
2348+   
2349+    <!-- Get the list of known UIDs -->
2350+    <method name="GetUserList">
2351+      <arg name="uids" direction="out" type="ax"/>
2352+    </method>
2353+
2354+    <!-- Get user info for a user -->
2355+    <method name="GetUserInfo">
2356+      <arg name="uid" direction="in" type="x"/>
2357+      <arg name="user_name" direction="out" type="s"/>
2358+      <arg name="real_name" direction="out" type="s"/>
2359+      <arg name="shell" direction="out" type="s"/>
2360+      <arg name="login_count" direction="out" type="i"/>     
2361+      <arg name="icon_url" direction="out" type="s"/>
2362+    </method>
2363+
2364+    <!-- Get user info for a list of users -->
2365+    <method name="GetUsersInfo">
2366+      <arg name="uid" direction="in" type="ax"/>
2367+      <!-- (uid, user_name, real_name, shell, login_count, icon_url) -->
2368+      <arg name="user_info" direction="out" type="a(xsssis)"/>
2369+    </method>
2370+   
2371+    <!-- Query if the initial user list is loaded -->
2372+    <method name="GetUsersLoaded">
2373+      <arg name="is_loaded" direction="out" type="b"/>
2374+    </method>
2375+
2376+    <!-- Triggered when the initial user list is loaded -->
2377+    <signal name="UsersLoaded" />
2378+
2379+    <!-- Triggered when a users are added to/removed from the system.
2380+         Clients should monitor these signals as soon as they connect to
2381+         this object -->
2382+    <signal name="UserAdded">
2383+      <arg name="uid" type="x"/>
2384+    </signal>
2385+    <signal name="UserRemoved">
2386+      <arg name="uid" type="x"/>
2387+    </signal>
2388+
2389+    <!-- Triggered when a user has updated information -->
2390+    <signal name="UserUpdated">
2391+      <arg name="uid" type="x"/>
2392+    </signal>
2393+   
2394+  </interface>
2395+</node>
2396diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/gdm-user-private.h gdm-2.28.0.new/daemon/gdm-user-private.h
2397--- gdm-2.28.0/daemon/gdm-user-private.h        1970-01-01 10:00:00.000000000 +1000
2398+++ gdm-2.28.0.new/daemon/gdm-user-private.h    2009-10-14 18:30:51.000000000 +1100
2399@@ -0,0 +1,42 @@
2400+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2401+ *
2402+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
2403+ *
2404+ * This program is free software; you can redistribute it and/or modify
2405+ * it under the terms of the GNU General Public License as published by
2406+ * the Free Software Foundation; either version 2 of the License, or
2407+ * (at your option) any later version.
2408+ *
2409+ * This program is distributed in the hope that it will be useful,
2410+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2411+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2412+ * GNU General Public License for more details.
2413+ *
2414+ * You should have received a copy of the GNU General Public License
2415+ * along with this program; if not, write to the Free Software
2416+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2417+ */
2418+
2419+/*
2420+ * Private interfaces to the GdmUser object
2421+ */
2422+
2423+#ifndef __GDM_USER_PRIVATE__
2424+#define __GDM_USER_PRIVATE__ 1
2425+
2426+#include <pwd.h>
2427+
2428+#include "gdm-user.h"
2429+
2430+G_BEGIN_DECLS
2431+
2432+void _gdm_user_update           (GdmUser             *user,
2433+                                 const struct passwd *pwent);
2434+void _gdm_user_add_session      (GdmUser             *user,
2435+                                 const char          *session_id);
2436+void _gdm_user_remove_session   (GdmUser             *user,
2437+                                 const char          *session_id);
2438+
2439+G_END_DECLS
2440+
2441+#endif /* !__GDM_USER_PRIVATE__ */
2442diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/main.c gdm-2.28.0.new/daemon/main.c
2443--- gdm-2.28.0/daemon/main.c    2009-09-22 06:05:27.000000000 +1000
2444+++ gdm-2.28.0.new/daemon/main.c        2009-10-14 18:30:51.000000000 +1100
2445@@ -43,6 +43,7 @@
2446 #include <dbus/dbus-glib.h>
2447 #include <dbus/dbus-glib-lowlevel.h>
2448 
2449+#include "gdm-user-manager.h"
2450 #include "gdm-manager.h"
2451 #include "gdm-log.h"
2452 #include "gdm-common.h"
2453@@ -59,6 +60,7 @@
2454 
2455 extern char **environ;
2456 
2457+static GdmUserManager  *user_manager  = NULL;
2458 static GdmManager      *manager       = NULL;
2459 static GdmSettings     *settings      = NULL;
2460 static uid_t            gdm_uid       = -1;
2461@@ -576,6 +578,12 @@
2462                 goto out;
2463         }
2464 
2465+        user_manager = gdm_user_manager_new ();
2466+        if (user_manager == NULL) {
2467+                g_warning ("Could not construct user manager object");
2468+                exit (1);
2469+        }
2470+
2471         if (! gdm_settings_direct_init (settings, GDMCONFDIR "/gdm.schemas", "/")) {
2472                 g_warning ("Unable to initialize settings");
2473                 goto out;
2474diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/daemon/Makefile.am gdm-2.28.0.new/daemon/Makefile.am
2475--- gdm-2.28.0/daemon/Makefile.am       2009-09-22 06:05:27.000000000 +1000
2476+++ gdm-2.28.0.new/daemon/Makefile.am   2009-10-14 18:30:51.000000000 +1100
2477@@ -9,6 +9,7 @@
2478        -DDATADIR=\"$(datadir)\"                        \
2479        -DDMCONFDIR=\"$(dmconfdir)\"                    \
2480        -DGDMCONFDIR=\"$(gdmconfdir)\"                  \
2481+       -DCACHEDIR=\"$(cachedir)\"                      \
2482        -DLIBDIR=\"$(libdir)\"                          \
2483        -DLIBEXECDIR=\"$(libexecdir)\"                  \
2484        -DLOGDIR=\"$(logdir)\"                          \
2485@@ -34,6 +35,7 @@
2486        gdm-session-direct-glue.h               \
2487        gdm-manager-glue.h                      \
2488        gdm-display-glue.h                      \
2489+       gdm-user-manager-glue.h                 \
2490        gdm-xdmcp-greeter-display-glue.h        \
2491        gdm-xdmcp-chooser-display-glue.h        \
2492        gdm-static-display-glue.h               \
2493@@ -45,6 +47,8 @@
2494 
2495 gdm-manager-glue.h: gdm-manager.xml Makefile.am
2496        dbus-binding-tool --prefix=gdm_manager --mode=glib-server --output=gdm-manager-glue.h $(srcdir)/gdm-manager.xml
2497+gdm-user-manager-glue.h: gdm-user-manager.xml Makefile.am
2498+       dbus-binding-tool --prefix=gdm_user_manager --mode=glib-server --output=gdm-user-manager-glue.h $(srcdir)/gdm-user-manager.xml
2499 gdm-slave-glue.h: gdm-slave.xml Makefile.am
2500        dbus-binding-tool --prefix=gdm_slave --mode=glib-server --output=gdm-slave-glue.h $(srcdir)/gdm-slave.xml
2501 gdm-simple-slave-glue.h: gdm-simple-slave.xml Makefile.am
2502@@ -303,6 +307,11 @@
2503        gdm-product-display.h           \
2504        gdm-manager.c                   \
2505        gdm-manager.h                   \
2506+       gdm-user.c                      \
2507+       gdm-user.h                      \
2508+       gdm-user-private.h              \
2509+       gdm-user-manager.c              \
2510+       gdm-user-manager.h              \
2511        gdm-slave-proxy.c               \
2512        gdm-slave-proxy.h               \
2513        $(NULL)
2514@@ -365,6 +374,7 @@
2515        gdm-session-direct.xml          \
2516        gdm-manager.xml                 \
2517        gdm-display.xml                 \
2518+       gdm-user-manager.xml            \
2519        gdm-xdmcp-greeter-display.xml   \
2520        gdm-xdmcp-chooser-display.xml   \
2521        gdm-static-display.xml          \
2522diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/data/gdm.conf.in gdm-2.28.0.new/data/gdm.conf.in
2523--- gdm-2.28.0/data/gdm.conf.in 2009-10-14 18:30:50.000000000 +1100
2524+++ gdm-2.28.0.new/data/gdm.conf.in     2009-10-14 18:30:51.000000000 +1100
2525@@ -8,6 +8,8 @@
2526     <allow own="org.gnome.DisplayManager"/>
2527 
2528     <allow send_destination="org.gnome.DisplayManager"
2529+           send_interface="org.gnome.DisplayManager.UserManager"/>
2530+    <allow send_destination="org.gnome.DisplayManager"
2531            send_interface="org.gnome.DisplayManager.Manager"/>
2532     <allow send_destination="org.gnome.DisplayManager"
2533            send_interface="org.gnome.DisplayManager.Display"/>
2534@@ -28,6 +30,8 @@
2535 
2536   <policy context="default">
2537     <deny send_destination="org.gnome.DisplayManager"
2538+          send_interface="org.gnome.DisplayManager.UserManager"/>
2539+    <deny send_destination="org.gnome.DisplayManager"
2540           send_interface="org.gnome.DisplayManager.Manager"/>
2541     <deny send_destination="org.gnome.DisplayManager"
2542           send_interface="org.gnome.DisplayManager.Display"/>
2543@@ -71,6 +75,22 @@
2544     <allow send_destination="org.gnome.DisplayManager"
2545            send_interface="org.gnome.DisplayManager.LocalDisplayFactory"
2546            send_member="StartGuestSession"/>
2547+         
2548+    <allow send_destination="org.gnome.DisplayManager"
2549+           send_interface="org.gnome.DisplayManager.UserManager"
2550+           send_member="CountUsers"/>
2551+    <allow send_destination="org.gnome.DisplayManager"
2552+           send_interface="org.gnome.DisplayManager.UserManager"
2553+           send_member="GetUsersLoaded"/>
2554+    <allow send_destination="org.gnome.DisplayManager"
2555+           send_interface="org.gnome.DisplayManager.UserManager"
2556+           send_member="GetUserList"/>
2557+    <allow send_destination="org.gnome.DisplayManager"
2558+           send_interface="org.gnome.DisplayManager.UserManager"
2559+           send_member="GetUserInfo"/>
2560+    <allow send_destination="org.gnome.DisplayManager"
2561+           send_interface="org.gnome.DisplayManager.UserManager"
2562+           send_member="GetUsersInfo"/>
2563 
2564     <allow send_destination="org.gnome.DisplayManager"
2565            send_interface="org.gnome.DisplayManager.Manager"
2566@@ -80,6 +100,8 @@
2567 
2568   <policy user="@GDM_USERNAME@">
2569     <allow send_destination="org.gnome.DisplayManager"
2570+           send_interface="org.gnome.DisplayManager.UserManager"/> 
2571+    <allow send_destination="org.gnome.DisplayManager"
2572            send_interface="org.gnome.DisplayManager.Manager"/>
2573     <allow send_destination="org.gnome.DisplayManager"
2574            send_interface="org.gnome.DisplayManager.Display"/>
Note: See TracBrowser for help on using the repository browser.