source: proiecte/PPPP/gdm/debian/patches/09_gdmsetup.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: 133.9 KB
RevLine 
[134]1#
2# Description: Add GDM Setup
3# Ubuntu: https://bugs.launchpad.net/ubuntu/+source/gdm/+bug/395299
4# Upstream: http://bugzilla.gnome.org/show_bug.cgi?id=587750
5#
6diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/configure.ac gdm-2.28.0.new/configure.ac
7--- gdm-2.28.0/configure.ac     2009-09-22 16:42:24.000000000 +0200
8+++ gdm-2.28.0.new/configure.ac 2009-09-22 16:42:25.000000000 +0200
9@@ -107,6 +107,14 @@
10 AC_SUBST(GCONF_CFLAGS)
11 AC_SUBST(GCONF_LIBS)
12 
13+PKG_CHECK_MODULES(GDMSETUP,
14+        dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
15+        gtk+-2.0 >= $GTK_REQUIRED_VERSION
16+       gmodule-2.0
17+)
18+AC_SUBST(GDMSETUP_CFLAGS)
19+AC_SUBST(GDMSETUP_LIBS)
20+
21 PKG_CHECK_MODULES(SIMPLE_GREETER,
22         dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
23         gtk+-2.0 >= $GTK_REQUIRED_VERSION
24@@ -1352,12 +1360,14 @@
25 daemon/Makefile
26 docs/Makefile
27 gui/Makefile
28+gui/gdmsetup/Makefile
29 gui/simple-greeter/Makefile
30 gui/simple-greeter/libnotificationarea/Makefile
31 gui/simple-chooser/Makefile
32 gui/user-switch-applet/Makefile
33 utils/Makefile
34 data/gdm.conf
35+data/gdm.policy
36 data/Makefile
37 data/faces/Makefile
38 data/greeter-autostart/Makefile
39diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdmsetup.c gdm-2.28.0.new/gui/gdmsetup/gdmsetup.c
40--- gdm-2.28.0/gui/gdmsetup/gdmsetup.c  1970-01-01 01:00:00.000000000 +0100
41+++ gdm-2.28.0.new/gui/gdmsetup/gdmsetup.c      2009-09-22 16:42:25.000000000 +0200
42@@ -0,0 +1,484 @@
43+#ifdef HAVE_CONFIG_H
44+#include "config.h"
45+#endif
46+
47+#include <string.h>
48+#include <stdlib.h>
49+#include <gtk/gtk.h>
50+#include <gdk/gdkkeysyms.h>
51+#include <dbus/dbus-glib.h>
52+#include <glib/gi18n.h>
53+
54+#include "gdm-user-manager.h"
55+
56+#define MAX_USERS_IN_COMBO_BOX 20
57+
58+/* User interface */
59+static GtkBuilder *ui;
60+static GtkWidget *dialog, *unlock_button, *option_vbox;
61+static GtkWidget *user_combo, *user_entry, *delay_spin;
62+static GtkWidget *auto_login_radio, *login_delay_box, *login_delay_check;
63+
64+/* Timer to delay application of configuration */
65+static guint apply_timeout = 0;
66+
67+/* Flag to show when information is loaded */
68+static gboolean loaded = FALSE;
69+
70+/* Flag to show when information is loaded */
71+static gboolean locked = TRUE;
72+
73+/* True if the user selection method is a combo box, False if an entry */
74+static gboolean user_list_is_combo = TRUE;
75+
76+/* User information */
77+static GdmUserManager *user_manager;
78+
79+/* Proxy to GDM settings */
80+static DBusGProxy *proxy = NULL;
81+
82+
83+static gchar *
84+get_value (const gchar *key, gchar *def)
85+{
86+    gchar *value;
87+    GError *error = NULL;
88+   
89+    if (!dbus_g_proxy_call (proxy, "GetValue", &error,
90+                            G_TYPE_STRING, key, G_TYPE_INVALID,
91+                            G_TYPE_STRING, &value, G_TYPE_INVALID)) {
92+        g_warning ("Error calling GetValue('%s'): %s", key, error->message);
93+        return def;
94+    }
95+   
96+    return value;
97+}
98+
99+
100+static gboolean
101+set_value (const gchar *key, const gchar *value)
102+{
103+    GError *error = NULL;
104+   
105+    dbus_g_proxy_call (proxy, "SetValue", &error,
106+                       G_TYPE_STRING, key,
107+                       G_TYPE_STRING, value, G_TYPE_INVALID, G_TYPE_INVALID);
108+    if (error) {
109+        g_warning ("Error calling SetValue('%s', '%s'): %s", key, value, error->message);
110+        return FALSE;
111+    }
112+   
113+    return TRUE;
114+}
115+
116+
117+static gboolean
118+get_boolean_value (const gchar *key, gboolean def)
119+{
120+    gchar *value;
121+    gboolean result;
122+   
123+    value = get_value (key, NULL);
124+    if (!value)
125+        return def;
126+    result = strcmp (value, "true") == 0;
127+    g_free (value);
128+    return result;
129+}
130+
131+
132+static gint
133+get_integer_value (const gchar *key, gint def)
134+{
135+    gchar *value;
136+    gint result;
137+    char *end;
138+   
139+    value = get_value (key, NULL);
140+    if (!value || value[0] == '\0')
141+        result = def;
142+    else {
143+        result = strtol (value, &end, 10);
144+        if (*end != '\0')
145+            result = def;
146+    }
147+
148+    if (value)
149+        g_free (value);
150+    return result;
151+}
152+
153+
154+static gboolean
155+set_boolean_value (const gchar *key, gboolean value)
156+{
157+    return set_value (key, value ? "true" : "false");
158+}
159+
160+
161+static gboolean
162+set_integer_value (const gchar *key, gint value)
163+{
164+    char value_string[1024];
165+    snprintf(value_string, 1024, "%d", value);
166+    return set_value (key, value_string);
167+}
168+
169+
170+static void
171+update_config ()
172+{
173+    GtkTreeModel *model;
174+    GtkTreeIter iter;
175+    gchar *user = NULL;
176+    gint delay = 0;
177+    gboolean auto_login = FALSE, timed_login = FALSE, error = FALSE;
178+   
179+    if (apply_timeout != 0) {
180+        g_source_remove (apply_timeout);
181+        apply_timeout = 0;
182+    }
183+
184+    if (user_list_is_combo) {
185+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (user_combo));
186+        if (gtk_combo_box_get_active_iter (GTK_COMBO_BOX (user_combo), &iter))
187+            gtk_tree_model_get (model, &iter, 1, &user, -1);
188+    }
189+    else
190+        user = g_strdup (gtk_entry_get_text (GTK_ENTRY (user_entry)));
191+
192+    if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (auto_login_radio))) {
193+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (login_delay_check)))
194+            timed_login = TRUE;
195+        else
196+            auto_login = TRUE;
197+    }
198+   
199+    delay = gtk_spin_button_get_value_as_int (GTK_SPIN_BUTTON (delay_spin));
200+
201+    g_debug ("set user='%s', auto=%s, timed=%s, delay=%d",
202+             user,
203+             auto_login ? "True" : "False",
204+             timed_login ? "True" : "False", delay);
205+   
206+    if (!set_boolean_value ("daemon/TimedLoginEnable", timed_login) ||
207+        !set_boolean_value ("daemon/AutomaticLoginEnable", auto_login) ||
208+        !set_value ("daemon/TimedLogin", user) ||
209+        !set_value ("daemon/AutomaticLogin", user) ||   
210+        !set_integer_value ("daemon/TimedLoginDelay", delay))
211+        error = FALSE;
212+
213+    if (user)
214+        g_free (user);
215+}
216+
217+
218+G_MODULE_EXPORT
219+void
220+gdm_capplet_response_cb (GtkWidget *widget, gint response_id)
221+{
222+    if (response_id != 1)
223+        gtk_main_quit ();
224+}
225+
226+
227+static void
228+unlock_response_cb (DBusGProxy     *proxy,
229+                    DBusGProxyCall *call_id,
230+                    void           *user_data)
231+{
232+    gboolean is_unlocked;
233+    GError *error = NULL;
234+   
235+    dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_BOOLEAN, &is_unlocked, G_TYPE_INVALID);
236+    if (error) {
237+        g_warning ("Failed to unlock: %s", error->message);
238+        g_error_free (error);
239+        return;
240+    }
241+   
242+    if (!is_unlocked)
243+        return;
244+   
245+    locked = FALSE;
246+    gtk_widget_set_sensitive (unlock_button, FALSE);
247+    gtk_widget_set_sensitive (option_vbox, loaded && !locked);
248+}
249+
250+
251+G_MODULE_EXPORT
252+void
253+unlock_button_clicked_cb (GtkWidget *widget)
254+{
255+    dbus_g_proxy_begin_call (proxy, "Unlock", unlock_response_cb, NULL, NULL, G_TYPE_INVALID);
256+}
257+
258+
259+G_MODULE_EXPORT
260+void
261+login_delay_check_toggled_cb (GtkWidget *widget)
262+{
263+    gtk_widget_set_sensitive (delay_spin,
264+                              gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (login_delay_check)));
265+   
266+    if (loaded)
267+        update_config ();
268+}
269+
270+
271+static gboolean
272+delayed_apply_cb ()
273+{
274+    update_config ();
275+    return FALSE;
276+}
277+
278+
279+G_MODULE_EXPORT
280+void
281+apply_config_cb (GtkWidget *widget)
282+{
283+    if (loaded) {
284+        if (apply_timeout != 0)
285+            g_source_remove (apply_timeout);
286+        apply_timeout = g_timeout_add (200, (GSourceFunc)delayed_apply_cb, NULL);
287+    }
288+}
289+
290+
291+static void
292+init_login_delay ()
293+{
294+    gint delay;
295+   
296+    if (get_boolean_value ("daemon/TimedLoginEnable", FALSE))
297+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (login_delay_check), TRUE);
298+   
299+    delay = get_integer_value ("daemon/TimedLoginDelay", 30);
300+
301+    g_debug ("init delay=%d", delay);
302+
303+    gtk_spin_button_set_value (GTK_SPIN_BUTTON (delay_spin), delay);
304+}
305+
306+
307+G_MODULE_EXPORT
308+void
309+automatic_login_toggle_cb (GtkWidget *automatic_login_toggle)   
310+{
311+    gboolean automatic_login;
312+
313+    automatic_login = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (automatic_login_toggle));
314+    gtk_widget_set_sensitive (login_delay_box, automatic_login);
315+    gtk_widget_set_sensitive (user_combo, automatic_login);
316+    gtk_widget_set_sensitive (user_entry, automatic_login);
317+   
318+    if (loaded)
319+        update_config ();
320+}
321+
322+
323+G_MODULE_EXPORT
324+void
325+default_user_combo_box_changed_cb (void)
326+{
327+    if (loaded) {
328+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_login_radio), TRUE);
329+        update_config ();
330+    }
331+}
332+
333+
334+static void
335+init_default_user (void)
336+{
337+    GtkTreeModel *model;
338+    GtkTreeIter iter;
339+    gboolean auto_login, timed_login, active;
340+    gchar *user = NULL;
341+   
342+    auto_login = get_boolean_value ("daemon/AutomaticLoginEnable", FALSE);
343+    timed_login = get_boolean_value ("daemon/TimedLoginEnable", FALSE);
344+   
345+    if (auto_login)
346+        user = get_value ("daemon/AutomaticLogin", NULL);
347+    if (user == NULL)
348+        user = get_value ("daemon/TimedLogin", NULL);
349+
350+    g_debug ("init user='%s' auto=%s", user, auto_login || timed_login ? "True" : "False");
351+
352+    if (auto_login || timed_login)
353+        gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (auto_login_radio), TRUE);
354+   
355+    if (!user_list_is_combo) {
356+        if (user != NULL)
357+            gtk_entry_set_text (GTK_ENTRY (user_entry), user);
358+    }
359+    else {
360+        model = gtk_combo_box_get_model (GTK_COMBO_BOX (user_combo));
361+        active = gtk_tree_model_get_iter_first (model, &iter);
362+       
363+        /* If no user then use first available */
364+        if (user == NULL) {
365+            if (active)
366+                gtk_combo_box_set_active_iter (GTK_COMBO_BOX (user_combo), &iter);
367+        }
368+        else {
369+            while (user != NULL && active) {
370+                gchar *u;
371+                gboolean matched;
372+           
373+                gtk_tree_model_get (model, &iter, 1, &u, -1);
374+                matched = strcmp (user, u) == 0;
375+                g_free (u);
376+                if (matched) {
377+                    gtk_combo_box_set_active_iter (GTK_COMBO_BOX (user_combo), &iter);
378+                    break;
379+                }
380+           
381+                active = gtk_tree_model_iter_next (model, &iter);
382+            }
383+        }
384+    }
385+   
386+    g_free (user);
387+}
388+
389+
390+static void add_user (GdmUser *user)
391+{
392+    GtkListStore *model;
393+    GtkTreeIter iter;
394+    GString *label;   
395+
396+    model = GTK_LIST_STORE (gtk_builder_get_object (ui, "login_user_model"));
397+    gtk_list_store_append (model, &iter);
398+    label = g_string_new("");
399+    g_string_printf (label, "%s (%s)", gdm_user_get_real_name (user), gdm_user_get_user_name (user));
400+    gtk_list_store_set (model, &iter,
401+                        0, label->str,
402+                        1, gdm_user_get_user_name (user),
403+                        -1);
404+    g_string_free (label, TRUE);
405+}
406+
407+
408+static void
409+users_loaded_cb(GdmUserManager *manager)
410+{
411+    GSList *users, *item;
412+
413+    users = gdm_user_manager_list_users (user_manager);
414+   
415+    if (g_slist_length (users) > MAX_USERS_IN_COMBO_BOX) {
416+        user_list_is_combo = FALSE;
417+        gtk_widget_hide (user_combo);
418+        gtk_widget_show (user_entry);
419+    }
420+    else {
421+        for (item = users; item; item = item->next)
422+            add_user ((GdmUser *) item->data);
423+    }
424+
425+    init_default_user ();
426+
427+    loaded = TRUE;
428+    gtk_widget_set_sensitive (option_vbox, loaded && !locked);
429+}
430+
431+
432+static void
433+user_added_cb (GdmUserManager *manager, GdmUser *user)
434+{
435+    if (loaded)
436+        add_user (user);
437+}
438+
439+
440+static void
441+split_text (const gchar *text, const gchar *prefix_label_name, const gchar *suffix_label_name)
442+{
443+    gchar **tokens;
444+    GtkWidget *prefix_label, *suffix_label;
445+   
446+    prefix_label = GTK_WIDGET (gtk_builder_get_object (ui, prefix_label_name));
447+    suffix_label = GTK_WIDGET (gtk_builder_get_object (ui, suffix_label_name));
448+   
449+    tokens = g_strsplit (text, "%s", 2);
450+    if (tokens[0] != NULL && tokens[1] != NULL) {
451+        if (tokens[0][0] != '\0')
452+            gtk_label_set_text (GTK_LABEL (prefix_label), g_strstrip (tokens[0]));
453+        else
454+            gtk_widget_hide (prefix_label);
455+        if (tokens[1][0] != '\0')
456+            gtk_label_set_text (GTK_LABEL (suffix_label), g_strstrip (tokens[1]));
457+        else
458+            gtk_widget_hide (suffix_label);
459+    }
460+    g_strfreev (tokens);
461+}
462+
463+
464+int main (int argc, char **argv)
465+{
466+    GtkCellRenderer *renderer;
467+    DBusGConnection *connection;   
468+    GError *error = NULL;
469+   
470+    bindtextdomain (GETTEXT_PACKAGE, GNOMELOCALEDIR);
471+    bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
472+    textdomain (GETTEXT_PACKAGE);
473+
474+    gtk_init (&argc, &argv);
475+   
476+    connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
477+    if (error) {
478+        g_warning ("Failed to get system bus: %s", error->message);
479+        return 1;
480+    }
481+    proxy = dbus_g_proxy_new_for_name (connection,
482+                                       "org.gnome.DisplayManager",
483+                                       "/org/gnome/DisplayManager/Settings",
484+                                       "org.gnome.DisplayManager.Settings");
485+
486+    ui = gtk_builder_new ();
487+    gtk_builder_add_from_file (ui, UIDIR "/gdmsetup.ui", &error);
488+    if (error) {
489+        g_warning ("Failed to load UI: %s", error->message);
490+        return 1;
491+    }
492+    dialog = GTK_WIDGET (gtk_builder_get_object (ui, "gdm_capplet"));
493+    unlock_button = GTK_WIDGET (gtk_builder_get_object (ui, "unlock_button"));
494+    option_vbox = GTK_WIDGET (gtk_builder_get_object (ui, "gdm_capplet_vbox"));
495+    user_combo = GTK_WIDGET (gtk_builder_get_object (ui, "default_user_combo_box"));
496+    user_entry = GTK_WIDGET (gtk_builder_get_object (ui, "default_user_entry"));
497+    delay_spin = GTK_WIDGET (gtk_builder_get_object (ui, "login_delay_spin"));
498+    auto_login_radio = GTK_WIDGET (gtk_builder_get_object (ui, "automatic_login_radio"));
499+    login_delay_box = GTK_WIDGET (gtk_builder_get_object (ui, "login_delay_box"));
500+    login_delay_check = GTK_WIDGET (gtk_builder_get_object (ui, "login_delay_check"));
501+    gtk_builder_connect_signals (ui, NULL);
502+
503+    /* Translators: Label for choosing which user to log in as. '%s' is replaced with an input field. */
504+    split_text (_("Log in as %s automatically"), "user_prefix_label", "user_suffix_label");
505+    /* Translators: Label for choosing the login delay. '%s' is replaced with an input field. */
506+    split_text (_("Allow %s seconds for anyone else to log in first"), "delay_prefix_label", "delay_suffix_label");
507+   
508+    init_login_delay ();
509+
510+    renderer = gtk_cell_renderer_text_new ();
511+    gtk_cell_layout_pack_start (GTK_CELL_LAYOUT (user_combo), renderer, TRUE);
512+    gtk_cell_layout_add_attribute (GTK_CELL_LAYOUT (user_combo), renderer, "text", 0);
513+
514+    user_manager = gdm_user_manager_ref_default ();
515+    g_signal_connect (user_manager, "users-loaded", G_CALLBACK (users_loaded_cb), NULL);
516+    g_signal_connect (user_manager, "user-added", G_CALLBACK (user_added_cb), NULL);
517+   
518+    gtk_widget_hide (user_entry);
519+
520+    gtk_widget_set_sensitive (option_vbox, FALSE);
521+    gtk_widget_show (dialog);
522+
523+    gtk_main ();
524+   
525+    return 0;
526+}
527diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdmsetup.desktop.in gdm-2.28.0.new/gui/gdmsetup/gdmsetup.desktop.in
528--- gdm-2.28.0/gui/gdmsetup/gdmsetup.desktop.in 1970-01-01 01:00:00.000000000 +0100
529+++ gdm-2.28.0.new/gui/gdmsetup/gdmsetup.desktop.in     2009-09-22 16:42:25.000000000 +0200
530@@ -0,0 +1,12 @@
531+[Desktop Entry]
532+_Name=Login Screen
533+_Comment=Configure login screen behavior
534+Exec=gdmsetup
535+Icon=gdm-setup
536+StartupNotify=true
537+Terminal=false
538+Type=Application
539+Categories=GNOME;GTK;Settings;System;
540+X-GNOME-Bugzilla-Bugzilla=GNOME
541+X-GNOME-Bugzilla-Product=gdm
542+X-GNOME-Bugzilla-Component=general
543diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdmsetup.ui gdm-2.28.0.new/gui/gdmsetup/gdmsetup.ui
544--- gdm-2.28.0/gui/gdmsetup/gdmsetup.ui 1970-01-01 01:00:00.000000000 +0100
545+++ gdm-2.28.0.new/gui/gdmsetup/gdmsetup.ui     2009-09-22 16:42:25.000000000 +0200
546@@ -0,0 +1,271 @@
547+<?xml version="1.0"?>
548+<interface>
549+  <requires lib="gtk+" version="2.16"/>
550+  <!-- interface-naming-policy project-wide -->
551+  <object class="GtkListStore" id="login_user_model">
552+    <columns>
553+      <!-- column-name label -->
554+      <column type="gchararray"/>
555+      <!-- column-name user -->
556+      <column type="gchararray"/>
557+      <!-- column-name icon -->
558+      <column type="GdkPixbuf"/>
559+    </columns>
560+  </object>
561+  <object class="GtkDialog" id="gdm_capplet">
562+    <property name="border_width">5</property>
563+    <property name="title" translatable="yes" comments="Title of login screen settings dialog">Login Screen Settings</property>
564+    <property name="resizable">False</property>
565+    <property name="window_position">center</property>
566+    <property name="icon_name">gdm-setup</property>
567+    <property name="type_hint">normal</property>
568+    <property name="has_separator">False</property>
569+    <signal name="response" handler="gdm_capplet_response_cb"/>
570+    <child internal-child="vbox">
571+      <object class="GtkVBox" id="dialog-vbox1">
572+        <property name="visible">True</property>
573+        <property name="orientation">vertical</property>
574+        <property name="spacing">12</property>
575+        <child>
576+          <object class="GtkVBox" id="gdm_capplet_vbox">
577+            <property name="visible">True</property>
578+            <property name="orientation">vertical</property>
579+            <property name="spacing">18</property>
580+            <child>
581+              <object class="GtkLabel" id="intro_label">
582+                <property name="visible">True</property>
583+                <property name="xalign">0</property>
584+                <property name="label" translatable="yes" comments="Description above login option radio buttons">When the computer starts up:</property>
585+              </object>
586+              <packing>
587+                <property name="expand">False</property>
588+                <property name="position">0</property>
589+              </packing>
590+            </child>
591+            <child>
592+              <object class="GtkVBox" id="vbox3">
593+                <property name="visible">True</property>
594+                <property name="orientation">vertical</property>
595+                <property name="spacing">12</property>
596+                <child>
597+                  <object class="GtkRadioButton" id="manual_login_radio">
598+                    <property name="label" translatable="yes" comments="Radio option to set login screen to prompt for user">_Show the screen for choosing who will log in</property>
599+                    <property name="visible">True</property>
600+                    <property name="can_focus">True</property>
601+                    <property name="receives_default">False</property>
602+                    <property name="use_underline">True</property>
603+                    <property name="active">True</property>
604+                    <property name="draw_indicator">True</property>
605+                  </object>
606+                  <packing>
607+                    <property name="expand">False</property>
608+                    <property name="position">0</property>
609+                  </packing>
610+                </child>
611+                <child>
612+                  <object class="GtkVBox" id="vbox4">
613+                    <property name="visible">True</property>
614+                    <property name="orientation">vertical</property>
615+                    <property name="spacing">6</property>
616+                    <child>
617+                      <object class="GtkHBox" id="hbox1">
618+                        <property name="visible">True</property>
619+                        <property name="spacing">6</property>
620+                        <child>
621+                          <object class="GtkRadioButton" id="automatic_login_radio">
622+                            <property name="visible">True</property>
623+                            <property name="can_focus">True</property>
624+                            <property name="receives_default">False</property>
625+                            <property name="draw_indicator">True</property>
626+                            <property name="group">manual_login_radio</property>
627+                            <signal name="toggled" handler="automatic_login_toggle_cb"/>
628+                            <child>
629+                              <object class="GtkLabel" id="user_prefix_label">
630+                                <property name="visible">True</property>
631+                                <property name="label">_Log in as</property>
632+                                <property name="use_underline">True</property>
633+                              </object>
634+                            </child>
635+                          </object>
636+                          <packing>
637+                            <property name="expand">False</property>
638+                            <property name="position">0</property>
639+                          </packing>
640+                        </child>
641+                        <child>
642+                          <object class="GtkComboBox" id="default_user_combo_box">
643+                            <property name="visible">True</property>
644+                            <property name="sensitive">False</property>
645+                            <property name="model">login_user_model</property>
646+                            <signal name="changed" handler="default_user_combo_box_changed_cb"/>
647+                          </object>
648+                          <packing>
649+                            <property name="expand">False</property>
650+                            <property name="position">1</property>
651+                          </packing>
652+                        </child>
653+                        <child>
654+                          <object class="GtkEntry" id="default_user_entry">
655+                            <property name="visible">True</property>
656+                            <property name="sensitive">False</property>
657+                            <property name="can_focus">True</property>
658+                            <property name="invisible_char">&#x2022;</property>
659+                            <signal name="changed" handler="apply_config_cb"/>
660+                          </object>
661+                          <packing>
662+                            <property name="position">2</property>
663+                          </packing>
664+                        </child>
665+                        <child>
666+                          <object class="GtkLabel" id="user_suffix_label">
667+                            <property name="visible">True</property>
668+                            <property name="label">automatically</property>
669+                          </object>
670+                          <packing>
671+                            <property name="expand">False</property>
672+                            <property name="position">3</property>
673+                          </packing>
674+                        </child>
675+                      </object>
676+                      <packing>
677+                        <property name="expand">False</property>
678+                        <property name="position">0</property>
679+                      </packing>
680+                    </child>
681+                    <child>
682+                      <object class="GtkAlignment" id="alignment1">
683+                        <property name="visible">True</property>
684+                        <property name="left_padding">16</property>
685+                        <child>
686+                          <object class="GtkHBox" id="login_delay_box">
687+                            <property name="visible">True</property>
688+                            <property name="sensitive">False</property>
689+                            <property name="spacing">6</property>
690+                            <child>
691+                              <object class="GtkCheckButton" id="login_delay_check">
692+                                <property name="visible">True</property>
693+                                <property name="can_focus">True</property>
694+                                <property name="receives_default">False</property>
695+                                <property name="draw_indicator">True</property>
696+                                <signal name="toggled" handler="login_delay_check_toggled_cb"/>
697+                                <child>
698+                                  <object class="GtkLabel" id="delay_prefix_label">
699+                                    <property name="visible">True</property>
700+                                    <property name="label">_Allow</property>
701+                                    <property name="use_underline">True</property>
702+                                  </object>
703+                                </child>
704+                              </object>
705+                              <packing>
706+                                <property name="expand">False</property>
707+                                <property name="position">0</property>
708+                              </packing>
709+                            </child>
710+                            <child>
711+                              <object class="GtkSpinButton" id="login_delay_spin">
712+                                <property name="visible">True</property>
713+                                <property name="sensitive">False</property>
714+                                <property name="can_focus">True</property>
715+                                <property name="invisible_char">&#x2022;</property>
716+                                <property name="adjustment">adjustment1</property>
717+                                <signal name="value_changed" handler="apply_config_cb"/>
718+                              </object>
719+                              <packing>
720+                                <property name="position">1</property>
721+                              </packing>
722+                            </child>
723+                            <child>
724+                              <object class="GtkLabel" id="delay_suffix_label">
725+                                <property name="visible">True</property>
726+                                <property name="label">seconds for anyone else to log in first</property>
727+                              </object>
728+                              <packing>
729+                                <property name="expand">False</property>
730+                                <property name="position">2</property>
731+                              </packing>
732+                            </child>
733+                          </object>
734+                        </child>
735+                      </object>
736+                      <packing>
737+                        <property name="expand">False</property>
738+                        <property name="position">1</property>
739+                      </packing>
740+                    </child>
741+                  </object>
742+                  <packing>
743+                    <property name="expand">False</property>
744+                    <property name="position">1</property>
745+                  </packing>
746+                </child>
747+              </object>
748+              <packing>
749+                <property name="expand">False</property>
750+                <property name="position">1</property>
751+              </packing>
752+            </child>
753+          </object>
754+          <packing>
755+            <property name="position">1</property>
756+          </packing>
757+        </child>
758+        <child internal-child="action_area">
759+          <object class="GtkHButtonBox" id="dialog-action_area1">
760+            <property name="visible">True</property>
761+            <property name="layout_style">end</property>
762+            <child>
763+              <object class="GtkButton" id="unlock_button">
764+                <property name="label" translatable="yes">_Unlock</property>
765+                <property name="visible">True</property>
766+                <property name="can_focus">True</property>
767+                <property name="receives_default">True</property>
768+                <property name="image">image1</property>
769+                <property name="use_underline">True</property>
770+                <signal name="clicked" handler="unlock_button_clicked_cb"/>
771+              </object>
772+              <packing>
773+                <property name="expand">False</property>
774+                <property name="fill">False</property>
775+                <property name="position">0</property>
776+              </packing>
777+            </child>
778+            <child>
779+              <object class="GtkButton" id="close_button">
780+                <property name="label">gtk-close</property>
781+                <property name="visible">True</property>
782+                <property name="can_focus">True</property>
783+                <property name="receives_default">True</property>
784+                <property name="use_stock">True</property>
785+              </object>
786+              <packing>
787+                <property name="expand">False</property>
788+                <property name="fill">False</property>
789+                <property name="position">1</property>
790+              </packing>
791+            </child>
792+          </object>
793+          <packing>
794+            <property name="expand">False</property>
795+            <property name="pack_type">end</property>
796+            <property name="position">0</property>
797+          </packing>
798+        </child>
799+      </object>
800+    </child>
801+    <action-widgets>
802+      <action-widget response="1">unlock_button</action-widget>
803+      <action-widget response="-7">close_button</action-widget>
804+    </action-widgets>
805+  </object>
806+  <object class="GtkImage" id="image1">
807+    <property name="visible">True</property>
808+    <property name="stock">gtk-dialog-authentication</property>
809+  </object>
810+  <object class="GtkSizeGroup" id="combo_size_group"/>
811+  <object class="GtkSizeGroup" id="radio_size_group"/>
812+  <object class="GtkAdjustment" id="adjustment1">
813+    <property name="lower">1</property>
814+    <property name="upper">600</property>
815+    <property name="step_increment">1</property>
816+  </object>
817+</interface>
818diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdm-user.c gdm-2.28.0.new/gui/gdmsetup/gdm-user.c
819--- gdm-2.28.0/gui/gdmsetup/gdm-user.c  1970-01-01 01:00:00.000000000 +0100
820+++ gdm-2.28.0.new/gui/gdmsetup/gdm-user.c      2009-09-22 16:42:25.000000000 +0200
821@@ -0,0 +1,1172 @@
822+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
823+ *
824+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
825+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
826+ *
827+ * This program is free software; you can redistribute it and/or modify
828+ * it under the terms of the GNU General Public License as published by
829+ * the Free Software Foundation; either version 2 of the License, or
830+ * (at your option) any later version.
831+ *
832+ * This program is distributed in the hope that it will be useful,
833+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
834+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
835+ * GNU General Public License for more details.
836+ *
837+ * You should have received a copy of the GNU General Public License
838+ * along with this program; if not, write to the Free Software
839+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
840+ */
841+
842+#include <config.h>
843+
844+#include <float.h>
845+#include <string.h>
846+#include <sys/types.h>
847+#include <sys/stat.h>
848+#include <unistd.h>
849+
850+#include <glib/gi18n.h>
851+#include <gio/gio.h>
852+#include <gtk/gtk.h>
853+
854+#include "gdm-user-manager.h"
855+#include "gdm-user-private.h"
856+
857+#define GDM_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_USER, GdmUserClass))
858+#define GDM_IS_USER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_USER))
859+#define GDM_USER_GET_CLASS(object) (G_TYPE_INSTANCE_GET_CLASS ((object), GDM_TYPE_USER, GdmUserClass))
860+
861+#define GLOBAL_FACEDIR    DATADIR "/faces"
862+#define MAX_ICON_SIZE     128
863+#define MAX_FILE_SIZE     65536
864+#define MINIMAL_UID       100
865+#define RELAX_GROUP       TRUE
866+#define RELAX_OTHER       TRUE
867+
868+enum {
869+        PROP_0,
870+        PROP_MANAGER,
871+        PROP_REAL_NAME,
872+        PROP_USER_NAME,
873+        PROP_UID,
874+        PROP_HOME_DIR,
875+        PROP_SHELL,
876+        PROP_LOGIN_FREQUENCY,
877+};
878+
879+enum {
880+        ICON_CHANGED,
881+        SESSIONS_CHANGED,
882+        LAST_SIGNAL
883+};
884+
885+struct _GdmUser {
886+        GObject         parent;
887+
888+        GdmUserManager *manager;
889+
890+        uid_t           uid;
891+        char           *user_name;
892+        char           *real_name;
893+        char           *home_dir;
894+        char           *shell;
895+        GList          *sessions;
896+        gulong          login_frequency;
897+
898+        GFileMonitor   *icon_monitor;
899+};
900+
901+typedef struct _GdmUserClass
902+{
903+        GObjectClass parent_class;
904+
905+        void (* icon_changed)     (GdmUser *user);
906+        void (* sessions_changed) (GdmUser *user);
907+} GdmUserClass;
908+
909+static void gdm_user_finalize     (GObject      *object);
910+
911+static guint signals[LAST_SIGNAL] = { 0 };
912+
913+G_DEFINE_TYPE (GdmUser, gdm_user, G_TYPE_OBJECT)
914+
915+static int
916+session_compare (const char *a,
917+                 const char *b)
918+{
919+        if (a == NULL) {
920+                return 1;
921+        } else if (b == NULL) {
922+                return -1;
923+        }
924+
925+        return strcmp (a, b);
926+}
927+
928+void
929+_gdm_user_add_session (GdmUser    *user,
930+                       const char *ssid)
931+{
932+        GList *li;
933+
934+        g_return_if_fail (GDM_IS_USER (user));
935+        g_return_if_fail (ssid != NULL);
936+
937+        li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
938+        if (li == NULL) {
939+                g_debug ("GdmUser: adding session %s", ssid);
940+                user->sessions = g_list_prepend (user->sessions, g_strdup (ssid));
941+                g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
942+        } else {
943+                g_debug ("GdmUser: session already present: %s", ssid);
944+        }
945+}
946+
947+void
948+_gdm_user_remove_session (GdmUser    *user,
949+                          const char *ssid)
950+{
951+        GList *li;
952+
953+        g_return_if_fail (GDM_IS_USER (user));
954+        g_return_if_fail (ssid != NULL);
955+
956+        li = g_list_find_custom (user->sessions, ssid, (GCompareFunc)session_compare);
957+        if (li != NULL) {
958+                g_debug ("GdmUser: removing session %s", ssid);
959+                g_free (li->data);
960+                user->sessions = g_list_delete_link (user->sessions, li);
961+                g_signal_emit (user, signals[SESSIONS_CHANGED], 0);
962+        } else {
963+                g_debug ("GdmUser: session not found: %s", ssid);
964+        }
965+}
966+
967+guint
968+gdm_user_get_num_sessions (GdmUser    *user)
969+{
970+        return g_list_length (user->sessions);
971+}
972+
973+GList *
974+gdm_user_get_sessions (GdmUser *user)
975+{
976+        return user->sessions;
977+}
978+
979+static void
980+_gdm_user_set_login_frequency (GdmUser *user,
981+                               gulong   login_frequency)
982+{
983+        user->login_frequency = login_frequency;
984+        g_object_notify (G_OBJECT (user), "login-frequency");
985+}
986+
987+static void
988+gdm_user_set_property (GObject      *object,
989+                       guint         param_id,
990+                       const GValue *value,
991+                       GParamSpec   *pspec)
992+{
993+        GdmUser *user;
994+
995+        user = GDM_USER (object);
996+
997+        switch (param_id) {
998+        case PROP_MANAGER:
999+                user->manager = g_value_get_object (value);
1000+                g_assert (user->manager);
1001+                break;
1002+        case PROP_LOGIN_FREQUENCY:
1003+                _gdm_user_set_login_frequency (user, g_value_get_ulong (value));
1004+                break;
1005+        default:
1006+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1007+                break;
1008+        }
1009+}
1010+
1011+static void
1012+gdm_user_get_property (GObject    *object,
1013+                       guint       param_id,
1014+                       GValue     *value,
1015+                       GParamSpec *pspec)
1016+{
1017+        GdmUser *user;
1018+
1019+        user = GDM_USER (object);
1020+
1021+        switch (param_id) {
1022+        case PROP_MANAGER:
1023+                g_value_set_object (value, user->manager);
1024+                break;
1025+        case PROP_USER_NAME:
1026+                g_value_set_string (value, user->user_name);
1027+                break;
1028+        case PROP_REAL_NAME:
1029+                g_value_set_string (value, user->real_name);
1030+                break;
1031+        case PROP_HOME_DIR:
1032+                g_value_set_string (value, user->home_dir);
1033+                break;
1034+        case PROP_UID:
1035+                g_value_set_ulong (value, user->uid);
1036+                break;
1037+        case PROP_SHELL:
1038+                g_value_set_string (value, user->shell);
1039+                break;
1040+        case PROP_LOGIN_FREQUENCY:
1041+                g_value_set_ulong (value, user->login_frequency);
1042+                break;
1043+        default:
1044+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
1045+                break;
1046+        }
1047+}
1048+
1049+static void
1050+gdm_user_class_init (GdmUserClass *class)
1051+{
1052+        GObjectClass *gobject_class;
1053+
1054+        gobject_class = G_OBJECT_CLASS (class);
1055+
1056+        gobject_class->set_property = gdm_user_set_property;
1057+        gobject_class->get_property = gdm_user_get_property;
1058+        gobject_class->finalize = gdm_user_finalize;
1059+
1060+        g_object_class_install_property (gobject_class,
1061+                                         PROP_MANAGER,
1062+                                         g_param_spec_object ("manager",
1063+                                                              _("Manager"),
1064+                                                              _("The user manager object this user is controlled by."),
1065+                                                              GDM_TYPE_USER_MANAGER,
1066+                                                              (G_PARAM_READWRITE |
1067+                                                               G_PARAM_CONSTRUCT_ONLY)));
1068+
1069+        g_object_class_install_property (gobject_class,
1070+                                         PROP_REAL_NAME,
1071+                                         g_param_spec_string ("real-name",
1072+                                                              "Real Name",
1073+                                                              "The real name to display for this user.",
1074+                                                              NULL,
1075+                                                              G_PARAM_READABLE));
1076+
1077+        g_object_class_install_property (gobject_class,
1078+                                         PROP_UID,
1079+                                         g_param_spec_ulong ("uid",
1080+                                                             "User ID",
1081+                                                             "The UID for this user.",
1082+                                                             0, G_MAXULONG, 0,
1083+                                                             G_PARAM_READABLE));
1084+        g_object_class_install_property (gobject_class,
1085+                                         PROP_USER_NAME,
1086+                                         g_param_spec_string ("user-name",
1087+                                                              "User Name",
1088+                                                              "The login name for this user.",
1089+                                                              NULL,
1090+                                                              G_PARAM_READABLE));
1091+        g_object_class_install_property (gobject_class,
1092+                                         PROP_HOME_DIR,
1093+                                         g_param_spec_string ("home-directory",
1094+                                                              "Home Directory",
1095+                                                              "The home directory for this user.",
1096+                                                              NULL,
1097+                                                              G_PARAM_READABLE));
1098+        g_object_class_install_property (gobject_class,
1099+                                         PROP_SHELL,
1100+                                         g_param_spec_string ("shell",
1101+                                                              "Shell",
1102+                                                              "The shell for this user.",
1103+                                                              NULL,
1104+                                                              G_PARAM_READABLE));
1105+        g_object_class_install_property (gobject_class,
1106+                                         PROP_LOGIN_FREQUENCY,
1107+                                         g_param_spec_ulong ("login-frequency",
1108+                                                             "login frequency",
1109+                                                             "login frequency",
1110+                                                             0,
1111+                                                             G_MAXULONG,
1112+                                                             0,
1113+                                                             G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
1114+
1115+        signals [ICON_CHANGED] =
1116+                g_signal_new ("icon-changed",
1117+                              G_TYPE_FROM_CLASS (class),
1118+                              G_SIGNAL_RUN_LAST,
1119+                              G_STRUCT_OFFSET (GdmUserClass, icon_changed),
1120+                              NULL, NULL,
1121+                              g_cclosure_marshal_VOID__VOID,
1122+                              G_TYPE_NONE, 0);
1123+        signals [SESSIONS_CHANGED] =
1124+                g_signal_new ("sessions-changed",
1125+                              G_TYPE_FROM_CLASS (class),
1126+                              G_SIGNAL_RUN_LAST,
1127+                              G_STRUCT_OFFSET (GdmUserClass, sessions_changed),
1128+                              NULL, NULL,
1129+                              g_cclosure_marshal_VOID__VOID,
1130+                              G_TYPE_NONE, 0);
1131+}
1132+
1133+
1134+static void
1135+on_icon_monitor_changed (GFileMonitor     *monitor,
1136+                         GFile            *file,
1137+                         GFile            *other_file,
1138+                         GFileMonitorEvent event_type,
1139+                         GdmUser          *user)
1140+{
1141+        g_debug ("Icon changed: %d", event_type);
1142+
1143+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
1144+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
1145+                return;
1146+        }
1147+
1148+        _gdm_user_icon_changed (user);
1149+}
1150+
1151+static void
1152+update_icon_monitor (GdmUser *user)
1153+{
1154+        GFile  *file;
1155+        GError *error;
1156+        char   *path;
1157+
1158+        if (user->home_dir == NULL) {
1159+                return;
1160+        }
1161+
1162+        if (user->icon_monitor != NULL) {
1163+                g_file_monitor_cancel (user->icon_monitor);
1164+                user->icon_monitor = NULL;
1165+        }
1166+
1167+        path = g_build_filename (user->home_dir, ".face", NULL);
1168+        g_debug ("adding monitor for '%s'", path);
1169+        file = g_file_new_for_path (path);
1170+        error = NULL;
1171+        user->icon_monitor = g_file_monitor_file (file,
1172+                                                  G_FILE_MONITOR_NONE,
1173+                                                  NULL,
1174+                                                  &error);
1175+        if (user->icon_monitor != NULL) {
1176+                g_signal_connect (user->icon_monitor,
1177+                                  "changed",
1178+                                  G_CALLBACK (on_icon_monitor_changed),
1179+                                  user);
1180+        } else {
1181+                g_warning ("Unable to monitor %s: %s", path, error->message);
1182+                g_error_free (error);
1183+        }
1184+        g_object_unref (file);
1185+        g_free (path);
1186+}
1187+
1188+static void
1189+gdm_user_init (GdmUser *user)
1190+{
1191+        user->manager = NULL;
1192+        user->user_name = NULL;
1193+        user->real_name = NULL;
1194+        user->sessions = NULL;
1195+}
1196+
1197+static void
1198+gdm_user_finalize (GObject *object)
1199+{
1200+        GdmUser *user;
1201+
1202+        user = GDM_USER (object);
1203+
1204+        g_file_monitor_cancel (user->icon_monitor);
1205+
1206+        g_free (user->user_name);
1207+        g_free (user->real_name);
1208+
1209+        if (G_OBJECT_CLASS (gdm_user_parent_class)->finalize)
1210+                (*G_OBJECT_CLASS (gdm_user_parent_class)->finalize) (object);
1211+}
1212+
1213+/**
1214+ * _gdm_user_update:
1215+ * @user: the user object to update.
1216+ * @pwent: the user data to use.
1217+ *
1218+ * Updates the properties of @user using the data in @pwent.
1219+ *
1220+ * Since: 1.0
1221+ **/
1222+void
1223+_gdm_user_update (GdmUser             *user,
1224+                  const struct passwd *pwent)
1225+{
1226+        gchar *real_name;
1227+
1228+        g_return_if_fail (GDM_IS_USER (user));
1229+        g_return_if_fail (pwent != NULL);
1230+
1231+        g_object_freeze_notify (G_OBJECT (user));
1232+
1233+        /* Display Name */
1234+        if (pwent->pw_gecos && pwent->pw_gecos[0] != '\0') {
1235+                gchar *first_comma;
1236+
1237+                first_comma = strchr (pwent->pw_gecos, ',');
1238+                if (first_comma) {
1239+                        real_name = g_strndup (pwent->pw_gecos,
1240+                                                  (first_comma - pwent->pw_gecos));
1241+                } else {
1242+                        real_name = g_strdup (pwent->pw_gecos);
1243+                }
1244+
1245+                if (real_name[0] == '\0') {
1246+                        g_free (real_name);
1247+                        real_name = NULL;
1248+                }
1249+        } else {
1250+                real_name = NULL;
1251+        }
1252+
1253+        if ((real_name && !user->real_name) ||
1254+            (!real_name && user->real_name) ||
1255+            (real_name &&
1256+             user->real_name &&
1257+             strcmp (real_name, user->real_name) != 0)) {
1258+                g_free (user->real_name);
1259+                user->real_name = real_name;
1260+                g_object_notify (G_OBJECT (user), "real-name");
1261+        } else {
1262+                g_free (real_name);
1263+        }
1264+
1265+        /* UID */
1266+        if (pwent->pw_uid != user->uid) {
1267+                user->uid = pwent->pw_uid;
1268+                g_object_notify (G_OBJECT (user), "uid");
1269+        }
1270+
1271+        /* Username */
1272+        if ((pwent->pw_name && !user->user_name) ||
1273+            (!pwent->pw_name && user->user_name) ||
1274+            (pwent->pw_name &&
1275+             user->user_name &&
1276+             strcmp (user->user_name, pwent->pw_name) != 0)) {
1277+                g_free (user->user_name);
1278+                user->user_name = g_strdup (pwent->pw_name);
1279+                g_object_notify (G_OBJECT (user), "user-name");
1280+        }
1281+
1282+        /* Home Directory */
1283+        if ((pwent->pw_dir && !user->home_dir) ||
1284+            (!pwent->pw_dir && user->home_dir) ||
1285+            strcmp (user->home_dir, pwent->pw_dir) != 0) {
1286+                g_free (user->home_dir);
1287+                user->home_dir = g_strdup (pwent->pw_dir);
1288+                g_object_notify (G_OBJECT (user), "home-directory");
1289+                g_signal_emit (user, signals[ICON_CHANGED], 0);
1290+        }
1291+
1292+        /* Shell */
1293+        if ((pwent->pw_shell && !user->shell) ||
1294+            (!pwent->pw_shell && user->shell) ||
1295+            (pwent->pw_shell &&
1296+             user->shell &&
1297+             strcmp (user->shell, pwent->pw_shell) != 0)) {
1298+                g_free (user->shell);
1299+                user->shell = g_strdup (pwent->pw_shell);
1300+                g_object_notify (G_OBJECT (user), "shell");
1301+        }
1302+
1303+        update_icon_monitor (user);
1304+
1305+        g_object_thaw_notify (G_OBJECT (user));
1306+}
1307+
1308+/**
1309+ * _gdm_user_icon_changed:
1310+ * @user: the user to emit the signal for.
1311+ *
1312+ * Emits the "icon-changed" signal for @user.
1313+ *
1314+ * Since: 1.0
1315+ **/
1316+void
1317+_gdm_user_icon_changed (GdmUser *user)
1318+{
1319+        g_return_if_fail (GDM_IS_USER (user));
1320+
1321+        g_signal_emit (user, signals[ICON_CHANGED], 0);
1322+}
1323+
1324+/**
1325+ * gdm_user_get_uid:
1326+ * @user: the user object to examine.
1327+ *
1328+ * Retrieves the ID of @user.
1329+ *
1330+ * Returns: a pointer to an array of characters which must not be modified or
1331+ *  freed, or %NULL.
1332+ *
1333+ * Since: 1.0
1334+ **/
1335+
1336+uid_t
1337+gdm_user_get_uid (GdmUser *user)
1338+{
1339+        g_return_val_if_fail (GDM_IS_USER (user), -1);
1340+
1341+        return user->uid;
1342+}
1343+
1344+/**
1345+ * gdm_user_get_real_name:
1346+ * @user: the user object to examine.
1347+ *
1348+ * Retrieves the display name of @user.
1349+ *
1350+ * Returns: a pointer to an array of characters which must not be modified or
1351+ *  freed, or %NULL.
1352+ *
1353+ * Since: 1.0
1354+ **/
1355+G_CONST_RETURN gchar *
1356+gdm_user_get_real_name (GdmUser *user)
1357+{
1358+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
1359+
1360+        return (user->real_name ? user->real_name : user->user_name);
1361+}
1362+
1363+/**
1364+ * gdm_user_get_user_name:
1365+ * @user: the user object to examine.
1366+ *
1367+ * Retrieves the login name of @user.
1368+ *
1369+ * Returns: a pointer to an array of characters which must not be modified or
1370+ *  freed, or %NULL.
1371+ *
1372+ * Since: 1.0
1373+ **/
1374+
1375+G_CONST_RETURN gchar *
1376+gdm_user_get_user_name (GdmUser *user)
1377+{
1378+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
1379+
1380+        return user->user_name;
1381+}
1382+
1383+/**
1384+ * gdm_user_get_home_directory:
1385+ * @user: the user object to examine.
1386+ *
1387+ * Retrieves the home directory of @user.
1388+ *
1389+ * Returns: a pointer to an array of characters which must not be modified or
1390+ *  freed, or %NULL.
1391+ *
1392+ * Since: 1.0
1393+ **/
1394+
1395+G_CONST_RETURN gchar *
1396+gdm_user_get_home_directory (GdmUser *user)
1397+{
1398+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
1399+
1400+        return user->home_dir;
1401+}
1402+
1403+/**
1404+ * gdm_user_get_shell:
1405+ * @user: the user object to examine.
1406+ *
1407+ * Retrieves the login shell of @user.
1408+ *
1409+ * Returns: a pointer to an array of characters which must not be modified or
1410+ *  freed, or %NULL.
1411+ *
1412+ * Since: 1.0
1413+ **/
1414+
1415+G_CONST_RETURN gchar *
1416+gdm_user_get_shell (GdmUser *user)
1417+{
1418+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
1419+
1420+        return user->shell;
1421+}
1422+
1423+gulong
1424+gdm_user_get_login_frequency (GdmUser *user)
1425+{
1426+        g_return_val_if_fail (GDM_IS_USER (user), 0);
1427+
1428+        return user->login_frequency;
1429+}
1430+
1431+int
1432+gdm_user_collate (GdmUser *user1,
1433+                  GdmUser *user2)
1434+{
1435+        const char *str1;
1436+        const char *str2;
1437+        gulong      num1;
1438+        gulong      num2;
1439+
1440+        g_return_val_if_fail (user1 == NULL || GDM_IS_USER (user1), 0);
1441+        g_return_val_if_fail (user2 == NULL || GDM_IS_USER (user2), 0);
1442+
1443+        if (user1->real_name != NULL) {
1444+                str1 = user1->real_name;
1445+        } else {
1446+                str1 = user1->user_name;
1447+        }
1448+
1449+        if (user2->real_name != NULL) {
1450+                str2 = user2->real_name;
1451+        } else {
1452+                str2 = user2->user_name;
1453+        }
1454+
1455+        num1 = user1->login_frequency;
1456+        num2 = user2->login_frequency;
1457+        g_debug ("Login freq 1=%u 2=%u", (guint)num1, (guint)num2);
1458+        if (num1 > num2) {
1459+                return -1;
1460+        }
1461+
1462+        if (num1 < num2) {
1463+                return 1;
1464+        }
1465+
1466+        /* if login frequency is equal try names */
1467+        if (str1 == NULL && str2 != NULL) {
1468+                return -1;
1469+        }
1470+
1471+        if (str1 != NULL && str2 == NULL) {
1472+                return 1;
1473+        }
1474+
1475+        if (str1 == NULL && str2 == NULL) {
1476+                return 0;
1477+        }
1478+
1479+        return g_utf8_collate (str1, str2);
1480+}
1481+
1482+static gboolean
1483+check_user_file (const char *filename,
1484+                 uid_t       user,
1485+                 gssize      max_file_size,
1486+                 gboolean    relax_group,
1487+                 gboolean    relax_other)
1488+{
1489+        struct stat fileinfo;
1490+
1491+        if (max_file_size < 0) {
1492+                max_file_size = G_MAXSIZE;
1493+        }
1494+
1495+        /* Exists/Readable? */
1496+        if (stat (filename, &fileinfo) < 0) {
1497+                return FALSE;
1498+        }
1499+
1500+        /* Is a regular file */
1501+        if (G_UNLIKELY (!S_ISREG (fileinfo.st_mode))) {
1502+                return FALSE;
1503+        }
1504+
1505+        /* Owned by user? */
1506+        if (G_UNLIKELY (fileinfo.st_uid != user)) {
1507+                return FALSE;
1508+        }
1509+
1510+        /* Group not writable or relax_group? */
1511+        if (G_UNLIKELY ((fileinfo.st_mode & S_IWGRP) == S_IWGRP && !relax_group)) {
1512+                return FALSE;
1513+        }
1514+
1515+        /* Other not writable or relax_other? */
1516+        if (G_UNLIKELY ((fileinfo.st_mode & S_IWOTH) == S_IWOTH && !relax_other)) {
1517+                return FALSE;
1518+        }
1519+
1520+        /* Size is kosher? */
1521+        if (G_UNLIKELY (fileinfo.st_size > max_file_size)) {
1522+                return FALSE;
1523+        }
1524+
1525+        return TRUE;
1526+}
1527+
1528+static char *
1529+get_filesystem_type (const char *path)
1530+{
1531+        GFile      *file;
1532+        GFileInfo  *file_info;
1533+        GError     *error;
1534+        char       *filesystem_type;
1535+
1536+        file = g_file_new_for_path (path);
1537+        error = NULL;
1538+        file_info = g_file_query_filesystem_info (file,
1539+                                                  G_FILE_ATTRIBUTE_FILESYSTEM_TYPE,
1540+                                                  NULL,
1541+                                                  &error);
1542+        if (file_info == NULL) {
1543+                g_warning ("Unable to query filesystem type for %s: %s", path, error->message);
1544+                g_error_free (error);
1545+                g_object_unref (file);
1546+                return NULL;
1547+        }
1548+
1549+        filesystem_type = g_strdup (g_file_info_get_attribute_string (file_info,
1550+                                                                      G_FILE_ATTRIBUTE_FILESYSTEM_TYPE));
1551+        if (filesystem_type == NULL) {
1552+                g_warning ("GIO returned NULL filesystem type for %s", path);
1553+        }
1554+
1555+        g_object_unref (file);
1556+        g_object_unref (file_info);
1557+
1558+        return filesystem_type;
1559+}
1560+
1561+static GdkPixbuf *
1562+render_icon_from_home (GdmUser *user,
1563+                       int      icon_size)
1564+{
1565+        GdkPixbuf  *retval;
1566+        char       *path;
1567+        gboolean    is_local;
1568+        gboolean    is_autofs;
1569+        gboolean    res;
1570+        char       *filesystem_type;
1571+
1572+        is_local = FALSE;
1573+
1574+        /* special case: look at parent of home to detect autofs
1575+           this is so we don't try to trigger an automount */
1576+        path = g_path_get_dirname (user->home_dir);
1577+        filesystem_type = get_filesystem_type (path);
1578+        is_autofs = (filesystem_type != NULL && strcmp (filesystem_type, "autofs") == 0);
1579+        g_free (filesystem_type);
1580+        g_free (path);
1581+
1582+        if (is_autofs) {
1583+                return NULL;
1584+        }
1585+
1586+        /* now check that home dir itself is local */
1587+        filesystem_type = get_filesystem_type (user->home_dir);
1588+        is_local = ((filesystem_type != NULL) &&
1589+                    (strcmp (filesystem_type, "nfs") != 0) &&
1590+                    (strcmp (filesystem_type, "afs") != 0) &&
1591+                    (strcmp (filesystem_type, "autofs") != 0) &&
1592+                    (strcmp (filesystem_type, "unknown") != 0) &&
1593+                    (strcmp (filesystem_type, "ncpfs") != 0));
1594+        g_free (filesystem_type);
1595+
1596+        /* only look at local home directories so we don't try to
1597+           read from remote (e.g. NFS) volumes */
1598+        if (! is_local) {
1599+                return NULL;
1600+        }
1601+
1602+        /* First, try "~/.face" */
1603+        path = g_build_filename (user->home_dir, ".face", NULL);
1604+        res = check_user_file (path,
1605+                               user->uid,
1606+                               MAX_FILE_SIZE,
1607+                               RELAX_GROUP,
1608+                               RELAX_OTHER);
1609+        if (res) {
1610+                retval = gdk_pixbuf_new_from_file_at_size (path,
1611+                                                           icon_size,
1612+                                                           icon_size,
1613+                                                           NULL);
1614+        } else {
1615+                retval = NULL;
1616+        }
1617+        g_free (path);
1618+
1619+        /* Next, try "~/.face.icon" */
1620+        if (retval == NULL) {
1621+                path = g_build_filename (user->home_dir,
1622+                                         ".face.icon",
1623+                                         NULL);
1624+                res = check_user_file (path,
1625+                                       user->uid,
1626+                                       MAX_FILE_SIZE,
1627+                                       RELAX_GROUP,
1628+                                       RELAX_OTHER);
1629+                if (res) {
1630+                        retval = gdk_pixbuf_new_from_file_at_size (path,
1631+                                                                   icon_size,
1632+                                                                   icon_size,
1633+                                                                   NULL);
1634+                } else {
1635+                        retval = NULL;
1636+                }
1637+
1638+                g_free (path);
1639+        }
1640+
1641+        /* Still nothing, try the user's personal GDM config */
1642+        if (retval == NULL) {
1643+                path = g_build_filename (user->home_dir,
1644+                                         ".gnome",
1645+                                         "gdm",
1646+                                         NULL);
1647+                res = check_user_file (path,
1648+                                       user->uid,
1649+                                       MAX_FILE_SIZE,
1650+                                       RELAX_GROUP,
1651+                                       RELAX_OTHER);
1652+                if (res) {
1653+                        GKeyFile *keyfile;
1654+                        char     *icon_path;
1655+
1656+                        keyfile = g_key_file_new ();
1657+                        g_key_file_load_from_file (keyfile,
1658+                                                   path,
1659+                                                   G_KEY_FILE_NONE,
1660+                                                   NULL);
1661+
1662+                        icon_path = g_key_file_get_string (keyfile,
1663+                                                           "face",
1664+                                                           "picture",
1665+                                                           NULL);
1666+                        res = check_user_file (icon_path,
1667+                                               user->uid,
1668+                                               MAX_FILE_SIZE,
1669+                                               RELAX_GROUP,
1670+                                               RELAX_OTHER);
1671+                        if (icon_path && res) {
1672+                                retval = gdk_pixbuf_new_from_file_at_size (path,
1673+                                                                           icon_size,
1674+                                                                           icon_size,
1675+                                                                           NULL);
1676+                        } else {
1677+                                retval = NULL;
1678+                        }
1679+
1680+                        g_free (icon_path);
1681+                        g_key_file_free (keyfile);
1682+                } else {
1683+                        retval = NULL;
1684+                }
1685+
1686+                g_free (path);
1687+        }
1688+
1689+        return retval;
1690+}
1691+
1692+static void
1693+curved_rectangle (cairo_t *cr,
1694+                  double   x0,
1695+                  double   y0,
1696+                  double   width,
1697+                  double   height,
1698+                  double   radius)
1699+{
1700+        double x1;
1701+        double y1;
1702+
1703+        x1 = x0 + width;
1704+        y1 = y0 + height;
1705+
1706+        if (width < FLT_EPSILON || height < FLT_EPSILON) {
1707+                return;
1708+        }
1709+
1710+        if (width / 2 < radius) {
1711+                if (height / 2 < radius) {
1712+                        cairo_move_to  (cr, x0, (y0 + y1) / 2);
1713+                        cairo_curve_to (cr, x0 ,y0, x0, y0, (x0 + x1) / 2, y0);
1714+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
1715+                        cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
1716+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
1717+                } else {
1718+                        cairo_move_to  (cr, x0, y0 + radius);
1719+                        cairo_curve_to (cr, x0, y0, x0, y0, (x0 + x1) / 2, y0);
1720+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
1721+                        cairo_line_to (cr, x1, y1 - radius);
1722+                        cairo_curve_to (cr, x1, y1, x1, y1, (x1 + x0) / 2, y1);
1723+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
1724+                }
1725+        } else {
1726+                if (height / 2 < radius) {
1727+                        cairo_move_to  (cr, x0, (y0 + y1) / 2);
1728+                        cairo_curve_to (cr, x0, y0, x0 , y0, x0 + radius, y0);
1729+                        cairo_line_to (cr, x1 - radius, y0);
1730+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, (y0 + y1) / 2);
1731+                        cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
1732+                        cairo_line_to (cr, x0 + radius, y1);
1733+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, (y0 + y1) / 2);
1734+                } else {
1735+                        cairo_move_to  (cr, x0, y0 + radius);
1736+                        cairo_curve_to (cr, x0 , y0, x0 , y0, x0 + radius, y0);
1737+                        cairo_line_to (cr, x1 - radius, y0);
1738+                        cairo_curve_to (cr, x1, y0, x1, y0, x1, y0 + radius);
1739+                        cairo_line_to (cr, x1, y1 - radius);
1740+                        cairo_curve_to (cr, x1, y1, x1, y1, x1 - radius, y1);
1741+                        cairo_line_to (cr, x0 + radius, y1);
1742+                        cairo_curve_to (cr, x0, y1, x0, y1, x0, y1 - radius);
1743+                }
1744+        }
1745+
1746+        cairo_close_path (cr);
1747+}
1748+
1749+static cairo_surface_t *
1750+surface_from_pixbuf (GdkPixbuf *pixbuf)
1751+{
1752+        cairo_surface_t *surface;
1753+        cairo_t         *cr;
1754+
1755+        surface = cairo_image_surface_create (gdk_pixbuf_get_has_alpha (pixbuf) ?
1756+                                              CAIRO_FORMAT_ARGB32 : CAIRO_FORMAT_RGB24,
1757+                                              gdk_pixbuf_get_width (pixbuf),
1758+                                              gdk_pixbuf_get_height (pixbuf));
1759+        cr = cairo_create (surface);
1760+        gdk_cairo_set_source_pixbuf (cr, pixbuf, 0, 0);
1761+        cairo_paint (cr);
1762+        cairo_destroy (cr);
1763+
1764+        return surface;
1765+}
1766+
1767+/**
1768+ * go_cairo_convert_data_to_pixbuf:
1769+ * @src: a pointer to pixel data in cairo format
1770+ * @dst: a pointer to pixel data in pixbuf format
1771+ * @width: image width
1772+ * @height: image height
1773+ * @rowstride: data rowstride
1774+ *
1775+ * Converts the pixel data stored in @src in CAIRO_FORMAT_ARGB32 cairo format
1776+ * to GDK_COLORSPACE_RGB pixbuf format and move them
1777+ * to @dst. If @src == @dst, pixel are converted in place.
1778+ **/
1779+
1780+static void
1781+go_cairo_convert_data_to_pixbuf (unsigned char *dst,
1782+                                 unsigned char const *src,
1783+                                 int width,
1784+                                 int height,
1785+                                 int rowstride)
1786+{
1787+        int i,j;
1788+        unsigned int t;
1789+        unsigned char a, b, c;
1790+
1791+        g_return_if_fail (dst != NULL);
1792+
1793+#define MULT(d,c,a,t) G_STMT_START { t = (a)? c * 255 / a: 0; d = t;} G_STMT_END
1794+
1795+        if (src == dst || src == NULL) {
1796+                for (i = 0; i < height; i++) {
1797+                        for (j = 0; j < width; j++) {
1798+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
1799+                                MULT(a, dst[2], dst[3], t);
1800+                                MULT(b, dst[1], dst[3], t);
1801+                                MULT(c, dst[0], dst[3], t);
1802+                                dst[0] = a;
1803+                                dst[1] = b;
1804+                                dst[2] = c;
1805+#else
1806+                                MULT(a, dst[1], dst[0], t);
1807+                                MULT(b, dst[2], dst[0], t);
1808+                                MULT(c, dst[3], dst[0], t);
1809+                                dst[3] = dst[0];
1810+                                dst[0] = a;
1811+                                dst[1] = b;
1812+                                dst[2] = c;
1813+#endif
1814+                                dst += 4;
1815+                        }
1816+                        dst += rowstride - width * 4;
1817+                }
1818+        } else {
1819+                for (i = 0; i < height; i++) {
1820+                        for (j = 0; j < width; j++) {
1821+#if G_BYTE_ORDER == G_LITTLE_ENDIAN
1822+                                MULT(dst[0], src[2], src[3], t);
1823+                                MULT(dst[1], src[1], src[3], t);
1824+                                MULT(dst[2], src[0], src[3], t);
1825+                                dst[3] = src[3];
1826+#else
1827+                                MULT(dst[0], src[1], src[0], t);
1828+                                MULT(dst[1], src[2], src[0], t);
1829+                                MULT(dst[2], src[3], src[0], t);
1830+                                dst[3] = src[0];
1831+#endif
1832+                                src += 4;
1833+                                dst += 4;
1834+                        }
1835+                        src += rowstride - width * 4;
1836+                        dst += rowstride - width * 4;
1837+                }
1838+        }
1839+#undef MULT
1840+}
1841+
1842+static void
1843+cairo_to_pixbuf (guint8    *src_data,
1844+                 GdkPixbuf *dst_pixbuf)
1845+{
1846+        unsigned char *src;
1847+        unsigned char *dst;
1848+        guint          w;
1849+        guint          h;
1850+        guint          rowstride;
1851+
1852+        w = gdk_pixbuf_get_width (dst_pixbuf);
1853+        h = gdk_pixbuf_get_height (dst_pixbuf);
1854+        rowstride = gdk_pixbuf_get_rowstride (dst_pixbuf);
1855+
1856+        dst = gdk_pixbuf_get_pixels (dst_pixbuf);
1857+        src = src_data;
1858+
1859+        go_cairo_convert_data_to_pixbuf (dst, src, w, h, rowstride);
1860+}
1861+
1862+static GdkPixbuf *
1863+frame_pixbuf (GdkPixbuf *source)
1864+{
1865+        GdkPixbuf       *dest;
1866+        cairo_t         *cr;
1867+        cairo_surface_t *surface;
1868+        guint            w;
1869+        guint            h;
1870+        guint            rowstride;
1871+        int              frame_width;
1872+        double           radius;
1873+        guint8          *data;
1874+
1875+        frame_width = 2;
1876+
1877+        w = gdk_pixbuf_get_width (source) + frame_width * 2;
1878+        h = gdk_pixbuf_get_height (source) + frame_width * 2;
1879+        radius = w / 3.0;
1880+
1881+        dest = gdk_pixbuf_new (GDK_COLORSPACE_RGB,
1882+                               TRUE,
1883+                               8,
1884+                               w,
1885+                               h);
1886+        rowstride = gdk_pixbuf_get_rowstride (dest);
1887+
1888+
1889+        data = g_new0 (guint8, h * rowstride);
1890+
1891+        surface = cairo_image_surface_create_for_data (data,
1892+                                                       CAIRO_FORMAT_ARGB32,
1893+                                                       w,
1894+                                                       h,
1895+                                                       rowstride);
1896+        cr = cairo_create (surface);
1897+        cairo_surface_destroy (surface);
1898+
1899+        /* set up image */
1900+        cairo_rectangle (cr, 0, 0, w, h);
1901+        cairo_set_source_rgba (cr, 1.0, 1.0, 1.0, 0.0);
1902+        cairo_fill (cr);
1903+
1904+        curved_rectangle (cr, frame_width, frame_width,
1905+                          w - frame_width * 2, h - frame_width * 2,
1906+                          radius);
1907+        cairo_set_source_rgba (cr, 0.5, 0.5, 0.5, 0.3);
1908+        cairo_fill_preserve (cr);
1909+
1910+        surface = surface_from_pixbuf (source);
1911+        cairo_set_source_surface (cr, surface, frame_width, frame_width);
1912+        cairo_fill (cr);
1913+        cairo_surface_destroy (surface);
1914+
1915+        cairo_to_pixbuf (data, dest);
1916+
1917+        cairo_destroy (cr);
1918+        g_free (data);
1919+
1920+        return dest;
1921+}
1922+
1923+GdkPixbuf *
1924+gdm_user_render_icon (GdmUser   *user,
1925+                      gint       icon_size)
1926+{
1927+        GdkPixbuf    *pixbuf;
1928+        GdkPixbuf    *framed;
1929+        char         *path;
1930+        char         *tmp;
1931+        gboolean      res;
1932+
1933+        g_return_val_if_fail (GDM_IS_USER (user), NULL);
1934+        g_return_val_if_fail (icon_size > 12, NULL);
1935+
1936+        path = NULL;
1937+
1938+        pixbuf = render_icon_from_home (user, icon_size);
1939+        if (pixbuf != NULL) {
1940+                goto out;
1941+        }
1942+
1943+        /* Try ${GlobalFaceDir}/${username} */
1944+        path = g_build_filename (GLOBAL_FACEDIR, user->user_name, NULL);
1945+        res = check_user_file (path,
1946+                               user->uid,
1947+                               MAX_FILE_SIZE,
1948+                               RELAX_GROUP,
1949+                               RELAX_OTHER);
1950+        if (res) {
1951+                pixbuf = gdk_pixbuf_new_from_file_at_size (path,
1952+                                                           icon_size,
1953+                                                           icon_size,
1954+                                                           NULL);
1955+        } else {
1956+                pixbuf = NULL;
1957+        }
1958+
1959+        g_free (path);
1960+        if (pixbuf != NULL) {
1961+                goto out;
1962+        }
1963+
1964+        /* Finally, ${GlobalFaceDir}/${username}.png */
1965+        tmp = g_strconcat (user->user_name, ".png", NULL);
1966+        path = g_build_filename (GLOBAL_FACEDIR, tmp, NULL);
1967+        g_free (tmp);
1968+        res = check_user_file (path,
1969+                               user->uid,
1970+                               MAX_FILE_SIZE,
1971+                               RELAX_GROUP,
1972+                               RELAX_OTHER);
1973+        if (res) {
1974+                pixbuf = gdk_pixbuf_new_from_file_at_size (path,
1975+                                                           icon_size,
1976+                                                           icon_size,
1977+                                                           NULL);
1978+        } else {
1979+                pixbuf = NULL;
1980+        }
1981+        g_free (path);
1982+ out:
1983+
1984+        if (pixbuf != NULL) {
1985+                framed = frame_pixbuf (pixbuf);
1986+                if (framed != NULL) {
1987+                        g_object_unref (pixbuf);
1988+                        pixbuf = framed;
1989+                }
1990+        }
1991+
1992+        return pixbuf;
1993+}
1994diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdm-user.h gdm-2.28.0.new/gui/gdmsetup/gdm-user.h
1995--- gdm-2.28.0/gui/gdmsetup/gdm-user.h  1970-01-01 01:00:00.000000000 +0100
1996+++ gdm-2.28.0.new/gui/gdmsetup/gdm-user.h      2009-09-22 16:42:25.000000000 +0200
1997@@ -0,0 +1,59 @@
1998+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
1999+ *
2000+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
2001+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
2002+ *
2003+ * This program is free software; you can redistribute it and/or modify
2004+ * it under the terms of the GNU General Public License as published by
2005+ * the Free Software Foundation; either version 2 of the License, or
2006+ * (at your option) any later version.
2007+ *
2008+ * This program is distributed in the hope that it will be useful,
2009+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2010+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2011+ * GNU General Public License for more details.
2012+ *
2013+ * You should have received a copy of the GNU General Public License
2014+ * along with this program; if not, write to the Free Software
2015+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
2016+ */
2017+
2018+/*
2019+ * Facade object for user data, owned by GdmUserManager
2020+ */
2021+
2022+#ifndef __GDM_USER__
2023+#define __GDM_USER__ 1
2024+
2025+#include <sys/types.h>
2026+#include <gtk/gtk.h>
2027+#include <gdk-pixbuf/gdk-pixbuf.h>
2028+
2029+G_BEGIN_DECLS
2030+
2031+#define GDM_TYPE_USER (gdm_user_get_type ())
2032+#define GDM_USER(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_USER, GdmUser))
2033+#define GDM_IS_USER(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_USER))
2034+
2035+typedef struct _GdmUser GdmUser;
2036+
2037+GType                 gdm_user_get_type            (void) G_GNUC_CONST;
2038+
2039+uid_t                 gdm_user_get_uid             (GdmUser   *user);
2040+G_CONST_RETURN char  *gdm_user_get_user_name       (GdmUser   *user);
2041+G_CONST_RETURN char  *gdm_user_get_real_name       (GdmUser   *user);
2042+G_CONST_RETURN char  *gdm_user_get_home_directory  (GdmUser   *user);
2043+G_CONST_RETURN char  *gdm_user_get_shell           (GdmUser   *user);
2044+guint                 gdm_user_get_num_sessions    (GdmUser   *user);
2045+GList                *gdm_user_get_sessions        (GdmUser   *user);
2046+gulong                gdm_user_get_login_frequency (GdmUser   *user);
2047+
2048+GdkPixbuf            *gdm_user_render_icon         (GdmUser   *user,
2049+                                                    gint       icon_size);
2050+
2051+gint                  gdm_user_collate             (GdmUser   *user1,
2052+                                                    GdmUser   *user2);
2053+
2054+G_END_DECLS
2055+
2056+#endif
2057diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdm-user-manager.c gdm-2.28.0.new/gui/gdmsetup/gdm-user-manager.c
2058--- gdm-2.28.0/gui/gdmsetup/gdm-user-manager.c  1970-01-01 01:00:00.000000000 +0100
2059+++ gdm-2.28.0.new/gui/gdmsetup/gdm-user-manager.c      2009-09-22 16:42:25.000000000 +0200
2060@@ -0,0 +1,1658 @@
2061+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
2062+ *
2063+ * Copyright (C) 2007-2008 William Jon McCann <mccann@jhu.edu>
2064+ *
2065+ * This program is free software; you can redistribute it and/or modify
2066+ * it under the terms of the GNU General Public License as published by
2067+ * the Free Software Foundation; either version 2 of the License, or
2068+ * (at your option) any later version.
2069+ *
2070+ * This program is distributed in the hope that it will be useful,
2071+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2072+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2073+ * GNU General Public License for more details.
2074+ *
2075+ * You should have received a copy of the GNU General Public License
2076+ * along with this program; if not, write to the Free Software
2077+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
2078+ *
2079+ */
2080+
2081+#include "config.h"
2082+
2083+#include <stdlib.h>
2084+#include <stdio.h>
2085+#include <fcntl.h>
2086+#include <unistd.h>
2087+#include <string.h>
2088+#include <signal.h>
2089+#include <errno.h>
2090+#include <sys/stat.h>
2091+#include <sys/types.h>
2092+
2093+#ifdef HAVE_PATHS_H
2094+#include <paths.h>
2095+#endif /* HAVE_PATHS_H */
2096+
2097+#include <glib.h>
2098+#include <glib/gi18n.h>
2099+#include <glib/gstdio.h>
2100+#include <glib-object.h>
2101+#include <gio/gio.h>
2102+
2103+#include <dbus/dbus.h>
2104+#include <dbus/dbus-glib.h>
2105+#include <dbus/dbus-glib-lowlevel.h>
2106+
2107+#include "gdm-user-manager.h"
2108+#include "gdm-user-private.h"
2109+
2110+#define GDM_USER_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerPrivate))
2111+
2112+#define CK_NAME      "org.freedesktop.ConsoleKit"
2113+#define CK_PATH      "/org/freedesktop/ConsoleKit"
2114+#define CK_INTERFACE "org.freedesktop.ConsoleKit"
2115+
2116+#define CK_MANAGER_PATH      "/org/freedesktop/ConsoleKit/Manager"
2117+#define CK_MANAGER_INTERFACE "org.freedesktop.ConsoleKit.Manager"
2118+#define CK_SEAT_INTERFACE    "org.freedesktop.ConsoleKit.Seat"
2119+#define CK_SESSION_INTERFACE "org.freedesktop.ConsoleKit.Session"
2120+
2121+/* Prefs Defaults */
2122+#define DEFAULT_ALLOW_ROOT      TRUE
2123+#define DEFAULT_MAX_ICON_SIZE   128
2124+#define DEFAULT_USER_MAX_FILE   65536
2125+
2126+#ifdef __sun
2127+#define DEFAULT_MINIMAL_UID     100
2128+#else
2129+#define DEFAULT_MINIMAL_UID     500
2130+#endif
2131+
2132+#ifndef _PATH_SHELLS
2133+#define _PATH_SHELLS    "/etc/shells"
2134+#endif
2135+#define PATH_PASSWD     "/etc/passwd"
2136+
2137+#define DEFAULT_GLOBAL_FACE_DIR DATADIR "/faces"
2138+#define DEFAULT_USER_ICON       "stock_person"
2139+#define DEFAULT_EXCLUDE         { "bin",        \
2140+                                  "root",       \
2141+                                  "daemon",     \
2142+                                  "adm",        \
2143+                                  "lp",         \
2144+                                  "sync",       \
2145+                                  "shutdown",   \
2146+                                  "halt",       \
2147+                                  "mail",       \
2148+                                  "news",       \
2149+                                  "uucp",       \
2150+                                  "operator",   \
2151+                                  "nobody",     \
2152+                                  GDM_USERNAME, \
2153+                                  "postgres",   \
2154+                                  "pvm",        \
2155+                                  "rpm",        \
2156+                                  "nfsnobody",  \
2157+                                  "pcap",       \
2158+                                  NULL }
2159+
2160+struct GdmUserManagerPrivate
2161+{
2162+        GHashTable            *users;
2163+        GHashTable            *sessions;
2164+        GHashTable            *exclusions;
2165+        GHashTable            *shells;
2166+        DBusGConnection       *connection;
2167+        DBusGProxy            *seat_proxy;
2168+        char                  *seat_id;
2169+
2170+        GFileMonitor          *passwd_monitor;
2171+        GFileMonitor          *shells_monitor;
2172+
2173+        guint                  reload_id;
2174+        guint                  ck_history_id;
2175+
2176+        guint8                 users_dirty : 1;
2177+};
2178+
2179+enum {
2180+        LOADING_USERS,
2181+        USERS_LOADED,
2182+        USER_ADDED,
2183+        USER_REMOVED,
2184+        USER_IS_LOGGED_IN_CHANGED,
2185+        USER_LOGIN_FREQUENCY_CHANGED,
2186+        LAST_SIGNAL
2187+};
2188+
2189+static guint signals [LAST_SIGNAL] = { 0, };
2190+
2191+static void     gdm_user_manager_class_init (GdmUserManagerClass *klass);
2192+static void     gdm_user_manager_init       (GdmUserManager      *user_manager);
2193+static void     gdm_user_manager_finalize   (GObject             *object);
2194+
2195+static gpointer user_manager_object = NULL;
2196+
2197+G_DEFINE_TYPE (GdmUserManager, gdm_user_manager, G_TYPE_OBJECT)
2198+
2199+GQuark
2200+gdm_user_manager_error_quark (void)
2201+{
2202+        static GQuark ret = 0;
2203+        if (ret == 0) {
2204+                ret = g_quark_from_static_string ("gdm_user_manager_error");
2205+        }
2206+
2207+        return ret;
2208+}
2209+
2210+static gboolean
2211+start_new_login_session (GdmUserManager *manager)
2212+{
2213+        GError  *error;
2214+        gboolean res;
2215+
2216+        res = g_spawn_command_line_async ("gdmflexiserver -s", &error);
2217+        if (! res) {
2218+                g_warning ("Unable to start new login: %s", error->message);
2219+                g_error_free (error);
2220+        }
2221+
2222+        return res;
2223+}
2224+
2225+/* needs to stay in sync with gdm-slave */
2226+static char *
2227+_get_primary_user_session_id (GdmUserManager *manager,
2228+                              GdmUser        *user)
2229+{
2230+        gboolean    res;
2231+        gboolean    can_activate_sessions;
2232+        GError     *error;
2233+        GList      *sessions;
2234+        GList      *l;
2235+        char       *primary_ssid;
2236+
2237+        if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
2238+                g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
2239+                return NULL;
2240+        }
2241+
2242+        primary_ssid = NULL;
2243+        sessions = NULL;
2244+
2245+        g_debug ("GdmUserManager: checking if seat can activate sessions");
2246+
2247+        error = NULL;
2248+        res = dbus_g_proxy_call (manager->priv->seat_proxy,
2249+                                 "CanActivateSessions",
2250+                                 &error,
2251+                                 G_TYPE_INVALID,
2252+                                 G_TYPE_BOOLEAN, &can_activate_sessions,
2253+                                 G_TYPE_INVALID);
2254+        if (! res) {
2255+                g_warning ("unable to determine if seat can activate sessions: %s",
2256+                           error->message);
2257+                g_error_free (error);
2258+                goto out;
2259+        }
2260+
2261+        if (! can_activate_sessions) {
2262+                g_debug ("GdmUserManager: seat is unable to activate sessions");
2263+                goto out;
2264+        }
2265+
2266+        sessions = gdm_user_get_sessions (user);
2267+        if (sessions == NULL) {
2268+                g_warning ("unable to determine sessions for user: %s",
2269+                           gdm_user_get_user_name (user));
2270+                goto out;
2271+        }
2272+
2273+        for (l = sessions; l != NULL; l = l->next) {
2274+                const char *ssid;
2275+
2276+                ssid = l->data;
2277+
2278+                /* FIXME: better way to choose? */
2279+                if (ssid != NULL) {
2280+                        primary_ssid = g_strdup (ssid);
2281+                        break;
2282+                }
2283+        }
2284+
2285+ out:
2286+
2287+        return primary_ssid;
2288+}
2289+
2290+static gboolean
2291+activate_session_id (GdmUserManager *manager,
2292+                     const char     *seat_id,
2293+                     const char     *session_id)
2294+{
2295+        DBusError    local_error;
2296+        DBusMessage *message;
2297+        DBusMessage *reply;
2298+        gboolean     ret;
2299+
2300+        ret = FALSE;
2301+        reply = NULL;
2302+
2303+        dbus_error_init (&local_error);
2304+        message = dbus_message_new_method_call ("org.freedesktop.ConsoleKit",
2305+                                                seat_id,
2306+                                                "org.freedesktop.ConsoleKit.Seat",
2307+                                                "ActivateSession");
2308+        if (message == NULL) {
2309+                goto out;
2310+        }
2311+
2312+        if (! dbus_message_append_args (message,
2313+                                        DBUS_TYPE_OBJECT_PATH, &session_id,
2314+                                        DBUS_TYPE_INVALID)) {
2315+                goto out;
2316+        }
2317+
2318+
2319+        dbus_error_init (&local_error);
2320+        reply = dbus_connection_send_with_reply_and_block (dbus_g_connection_get_connection (manager->priv->connection),
2321+                                                           message,
2322+                                                           -1,
2323+                                                           &local_error);
2324+        if (reply == NULL) {
2325+                if (dbus_error_is_set (&local_error)) {
2326+                        g_warning ("Unable to activate session: %s", local_error.message);
2327+                        dbus_error_free (&local_error);
2328+                        goto out;
2329+                }
2330+        }
2331+
2332+        ret = TRUE;
2333+ out:
2334+        if (message != NULL) {
2335+                dbus_message_unref (message);
2336+        }
2337+        if (reply != NULL) {
2338+                dbus_message_unref (reply);
2339+        }
2340+
2341+        return ret;
2342+}
2343+
2344+static gboolean
2345+session_is_login_window (GdmUserManager *manager,
2346+                         const char     *session_id)
2347+{
2348+        DBusGProxy      *proxy;
2349+        GError          *error;
2350+        gboolean         res;
2351+        gboolean         ret;
2352+        char            *session_type;
2353+
2354+        ret = FALSE;
2355+
2356+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
2357+                                           CK_NAME,
2358+                                           session_id,
2359+                                           CK_SESSION_INTERFACE);
2360+        if (proxy == NULL) {
2361+                g_warning ("Failed to connect to the ConsoleKit seat object");
2362+                goto out;
2363+        }
2364+
2365+        session_type = NULL;
2366+        error = NULL;
2367+        res = dbus_g_proxy_call (proxy,
2368+                                 "GetSessionType",
2369+                                 &error,
2370+                                 G_TYPE_INVALID,
2371+                                 G_TYPE_STRING, &session_type,
2372+                                 G_TYPE_INVALID);
2373+        if (! res) {
2374+                g_debug ("Failed to identify the session type: %s", error->message);
2375+                g_error_free (error);
2376+                goto out;
2377+        }
2378+
2379+        if (session_type == NULL || session_type[0] == '\0' || strcmp (session_type, "LoginWindow") != 0) {
2380+                goto out;
2381+        }
2382+
2383+        ret = TRUE;
2384+
2385+ out:
2386+        if (proxy != NULL) {
2387+                g_object_unref (proxy);
2388+        }
2389+
2390+        return ret;
2391+}
2392+
2393+static char *
2394+_get_login_window_session_id (GdmUserManager *manager)
2395+{
2396+        gboolean    res;
2397+        gboolean    can_activate_sessions;
2398+        GError     *error;
2399+        GPtrArray  *sessions;
2400+        char       *primary_ssid;
2401+        int         i;
2402+
2403+        if (manager->priv->seat_id == NULL || manager->priv->seat_id[0] == '\0') {
2404+                g_debug ("GdmUserManager: display seat id is not set; can't switch sessions");
2405+                return NULL;
2406+        }
2407+
2408+        primary_ssid = NULL;
2409+        sessions = NULL;
2410+
2411+        g_debug ("GdmSlave: checking if seat can activate sessions");
2412+
2413+        error = NULL;
2414+        res = dbus_g_proxy_call (manager->priv->seat_proxy,
2415+                                 "CanActivateSessions",
2416+                                 &error,
2417+                                 G_TYPE_INVALID,
2418+                                 G_TYPE_BOOLEAN, &can_activate_sessions,
2419+                                 G_TYPE_INVALID);
2420+        if (! res) {
2421+                g_warning ("unable to determine if seat can activate sessions: %s",
2422+                           error->message);
2423+                g_error_free (error);
2424+                goto out;
2425+        }
2426+
2427+        if (! can_activate_sessions) {
2428+                g_debug ("GdmSlave: seat is unable to activate sessions");
2429+                goto out;
2430+        }
2431+
2432+        error = NULL;
2433+        res = dbus_g_proxy_call (manager->priv->seat_proxy,
2434+                                 "GetSessions",
2435+                                 &error,
2436+                                 G_TYPE_INVALID,
2437+                                 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH), &sessions,
2438+                                 G_TYPE_INVALID);
2439+        if (! res) {
2440+                g_warning ("unable to determine sessions for user: %s",
2441+                           error->message);
2442+                g_error_free (error);
2443+                goto out;
2444+        }
2445+
2446+        for (i = 0; i < sessions->len; i++) {
2447+                char *ssid;
2448+
2449+                ssid = g_ptr_array_index (sessions, i);
2450+
2451+                if (session_is_login_window (manager, ssid)) {
2452+                        primary_ssid = g_strdup (ssid);
2453+                        break;
2454+                }
2455+        }
2456+        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
2457+        g_ptr_array_free (sessions, TRUE);
2458+
2459+ out:
2460+
2461+        return primary_ssid;
2462+}
2463+
2464+gboolean
2465+gdm_user_manager_goto_login_session (GdmUserManager *manager)
2466+{
2467+        gboolean ret;
2468+        gboolean res;
2469+        char    *ssid;
2470+
2471+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
2472+
2473+        ret = FALSE;
2474+
2475+        /* First look for any existing LoginWindow sessions on the seat.
2476+           If none are found, create a new one. */
2477+
2478+        ssid = _get_login_window_session_id (manager);
2479+        if (ssid != NULL) {
2480+                res = activate_session_id (manager, manager->priv->seat_id, ssid);
2481+                if (res) {
2482+                        ret = TRUE;
2483+                }
2484+        }
2485+
2486+        if (! ret) {
2487+                res = start_new_login_session (manager);
2488+                if (res) {
2489+                        ret = TRUE;
2490+                }
2491+        }
2492+
2493+        return ret;
2494+}
2495+
2496+gboolean
2497+gdm_user_manager_activate_user_session (GdmUserManager *manager,
2498+                                        GdmUser        *user)
2499+{
2500+        gboolean ret;
2501+        char    *ssid;
2502+        gboolean res;
2503+
2504+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), FALSE);
2505+        g_return_val_if_fail (GDM_IS_USER (user), FALSE);
2506+
2507+        ret = FALSE;
2508+
2509+        ssid = _get_primary_user_session_id (manager, user);
2510+        if (ssid == NULL) {
2511+                goto out;
2512+        }
2513+
2514+        res = activate_session_id (manager, manager->priv->seat_id, ssid);
2515+        if (! res) {
2516+                g_debug ("GdmUserManager: unable to activate session: %s", ssid);
2517+                goto out;
2518+        }
2519+
2520+        ret = TRUE;
2521+ out:
2522+        return ret;
2523+}
2524+
2525+static void
2526+on_user_sessions_changed (GdmUser        *user,
2527+                          GdmUserManager *manager)
2528+{
2529+        guint nsessions;
2530+
2531+        nsessions = gdm_user_get_num_sessions (user);
2532+
2533+        g_debug ("GdmUserManager: sessions changed user=%s num=%d",
2534+                 gdm_user_get_user_name (user),
2535+                 nsessions);
2536+
2537+        /* only signal on zero and one */
2538+        if (nsessions > 1) {
2539+                return;
2540+        }
2541+
2542+        g_signal_emit (manager, signals [USER_IS_LOGGED_IN_CHANGED], 0, user);
2543+}
2544+
2545+static void
2546+on_user_icon_changed (GdmUser        *user,
2547+                      GdmUserManager *manager)
2548+{
2549+        g_debug ("GdmUserManager: user icon changed");
2550+}
2551+
2552+static char *
2553+get_seat_id_for_session (DBusGConnection *connection,
2554+                         const char      *session_id)
2555+{
2556+        DBusGProxy      *proxy;
2557+        GError          *error;
2558+        char            *seat_id;
2559+        gboolean         res;
2560+
2561+        proxy = NULL;
2562+        seat_id = NULL;
2563+
2564+        proxy = dbus_g_proxy_new_for_name (connection,
2565+                                           CK_NAME,
2566+                                           session_id,
2567+                                           CK_SESSION_INTERFACE);
2568+        if (proxy == NULL) {
2569+                g_warning ("Failed to connect to the ConsoleKit session object");
2570+                goto out;
2571+        }
2572+
2573+        error = NULL;
2574+        res = dbus_g_proxy_call (proxy,
2575+                                 "GetSeatId",
2576+                                 &error,
2577+                                 G_TYPE_INVALID,
2578+                                 DBUS_TYPE_G_OBJECT_PATH, &seat_id,
2579+                                 G_TYPE_INVALID);
2580+        if (! res) {
2581+                g_debug ("Failed to identify the current seat: %s", error->message);
2582+                g_error_free (error);
2583+        }
2584+ out:
2585+        if (proxy != NULL) {
2586+                g_object_unref (proxy);
2587+        }
2588+
2589+        return seat_id;
2590+}
2591+
2592+static char *
2593+get_x11_display_for_session (DBusGConnection *connection,
2594+                             const char      *session_id)
2595+{
2596+        DBusGProxy      *proxy;
2597+        GError          *error;
2598+        char            *x11_display;
2599+        gboolean         res;
2600+
2601+        proxy = NULL;
2602+        x11_display = NULL;
2603+
2604+        proxy = dbus_g_proxy_new_for_name (connection,
2605+                                           CK_NAME,
2606+                                           session_id,
2607+                                           CK_SESSION_INTERFACE);
2608+        if (proxy == NULL) {
2609+                g_warning ("Failed to connect to the ConsoleKit session object");
2610+                goto out;
2611+        }
2612+
2613+        error = NULL;
2614+        res = dbus_g_proxy_call (proxy,
2615+                                 "GetX11Display",
2616+                                 &error,
2617+                                 G_TYPE_INVALID,
2618+                                 G_TYPE_STRING, &x11_display,
2619+                                 G_TYPE_INVALID);
2620+        if (! res) {
2621+                g_debug ("Failed to identify the x11 display: %s", error->message);
2622+                g_error_free (error);
2623+        }
2624+ out:
2625+        if (proxy != NULL) {
2626+                g_object_unref (proxy);
2627+        }
2628+
2629+        return x11_display;
2630+}
2631+
2632+static gboolean
2633+maybe_add_session_for_user (GdmUserManager *manager,
2634+                            GdmUser        *user,
2635+                            const char     *ssid)
2636+{
2637+        char    *sid;
2638+        char    *x11_display;
2639+        gboolean ret;
2640+
2641+        ret = FALSE;
2642+        sid = NULL;
2643+        x11_display = NULL;
2644+
2645+        /* skip if on another seat */
2646+        sid = get_seat_id_for_session (manager->priv->connection, ssid);
2647+        if (sid == NULL
2648+            || manager->priv->seat_id == NULL
2649+            || strcmp (sid, manager->priv->seat_id) != 0) {
2650+                g_debug ("GdmUserManager: not adding session on other seat: %s", ssid);
2651+                goto out;
2652+        }
2653+
2654+        /* skip if doesn't have an x11 display */
2655+        x11_display = get_x11_display_for_session (manager->priv->connection, ssid);
2656+        if (x11_display == NULL || x11_display[0] == '\0') {
2657+                g_debug ("GdmUserManager: not adding session without a x11 display: %s", ssid);
2658+                goto out;
2659+        }
2660+
2661+        if (g_hash_table_lookup (manager->priv->exclusions, gdm_user_get_user_name (user))) {
2662+                g_debug ("GdmUserManager: excluding user '%s'", gdm_user_get_user_name (user));
2663+                goto out;
2664+        }
2665+
2666+        g_hash_table_insert (manager->priv->sessions,
2667+                             g_strdup (ssid),
2668+                             g_strdup (gdm_user_get_user_name (user)));
2669+
2670+        _gdm_user_add_session (user, ssid);
2671+        g_debug ("GdmUserManager: added session for user: %s", gdm_user_get_user_name (user));
2672+
2673+        ret = TRUE;
2674+
2675+ out:
2676+        g_free (sid);
2677+        g_free (x11_display);
2678+
2679+        return ret;
2680+}
2681+
2682+static void
2683+add_sessions_for_user (GdmUserManager *manager,
2684+                       GdmUser        *user)
2685+{
2686+        DBusGProxy      *proxy;
2687+        GError          *error;
2688+        gboolean         res;
2689+        guint32          uid;
2690+        GPtrArray       *sessions;
2691+        int              i;
2692+
2693+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
2694+                                           CK_NAME,
2695+                                           CK_MANAGER_PATH,
2696+                                           CK_MANAGER_INTERFACE);
2697+        if (proxy == NULL) {
2698+                g_warning ("Failed to connect to the ConsoleKit manager object");
2699+                goto out;
2700+        }
2701+
2702+        uid = gdm_user_get_uid (user);
2703+
2704+        g_debug ("Getting list of sessions for user %u", uid);
2705+
2706+        error = NULL;
2707+        res = dbus_g_proxy_call (proxy,
2708+                                 "GetSessionsForUnixUser",
2709+                                 &error,
2710+                                 G_TYPE_UINT, uid,
2711+                                 G_TYPE_INVALID,
2712+                                 dbus_g_type_get_collection ("GPtrArray", DBUS_TYPE_G_OBJECT_PATH),
2713+                                 &sessions,
2714+                                 G_TYPE_INVALID);
2715+        if (! res) {
2716+                g_debug ("Failed to find sessions for user: %s", error->message);
2717+                g_error_free (error);
2718+                goto out;
2719+        }
2720+
2721+        g_debug ("Found %d sessions for user %s", sessions->len, gdm_user_get_user_name (user));
2722+
2723+        for (i = 0; i < sessions->len; i++) {
2724+                char *ssid;
2725+
2726+                ssid = g_ptr_array_index (sessions, i);
2727+                maybe_add_session_for_user (manager, user, ssid);
2728+        }
2729+
2730+        g_ptr_array_foreach (sessions, (GFunc)g_free, NULL);
2731+        g_ptr_array_free (sessions, TRUE);
2732+
2733+ out:
2734+        if (proxy != NULL) {
2735+                g_object_unref (proxy);
2736+        }
2737+}
2738+
2739+static GdmUser *
2740+create_user (GdmUserManager *manager)
2741+{
2742+        GdmUser *user;
2743+
2744+        user = g_object_new (GDM_TYPE_USER, "manager", manager, NULL);
2745+        g_signal_connect (user,
2746+                          "sessions-changed",
2747+                          G_CALLBACK (on_user_sessions_changed),
2748+                          manager);
2749+        g_signal_connect (user,
2750+                          "icon-changed",
2751+                          G_CALLBACK (on_user_icon_changed),
2752+                          manager);
2753+        return user;
2754+}
2755+
2756+static void
2757+add_user (GdmUserManager *manager,
2758+          GdmUser        *user)
2759+{
2760+        add_sessions_for_user (manager, user);
2761+        g_hash_table_insert (manager->priv->users,
2762+                             g_strdup (gdm_user_get_user_name (user)),
2763+                             g_object_ref (user));
2764+
2765+        g_signal_emit (manager, signals[USER_ADDED], 0, user);
2766+}
2767+
2768+static GdmUser *
2769+add_new_user_for_pwent (GdmUserManager *manager,
2770+                        struct passwd  *pwent)
2771+{
2772+        GdmUser *user;
2773+
2774+        g_debug ("Creating new user");
2775+
2776+        user = create_user (manager);
2777+        _gdm_user_update (user, pwent);
2778+
2779+        add_user (manager, user);
2780+
2781+        return user;
2782+}
2783+
2784+static char *
2785+get_current_seat_id (DBusGConnection *connection)
2786+{
2787+        DBusGProxy      *proxy;
2788+        GError          *error;
2789+        char            *session_id;
2790+        char            *seat_id;
2791+        gboolean         res;
2792+
2793+        proxy = NULL;
2794+        session_id = NULL;
2795+        seat_id = NULL;
2796+
2797+        proxy = dbus_g_proxy_new_for_name (connection,
2798+                                           CK_NAME,
2799+                                           CK_MANAGER_PATH,
2800+                                           CK_MANAGER_INTERFACE);
2801+        if (proxy == NULL) {
2802+                g_warning ("Failed to connect to the ConsoleKit manager object");
2803+                goto out;
2804+        }
2805+
2806+        error = NULL;
2807+        res = dbus_g_proxy_call (proxy,
2808+                                 "GetCurrentSession",
2809+                                 &error,
2810+                                 G_TYPE_INVALID,
2811+                                 DBUS_TYPE_G_OBJECT_PATH,
2812+                                 &session_id,
2813+                                 G_TYPE_INVALID);
2814+        if (! res) {
2815+                g_debug ("Failed to identify the current session: %s", error->message);
2816+                g_error_free (error);
2817+                goto out;
2818+        }
2819+
2820+        seat_id = get_seat_id_for_session (connection, session_id);
2821+
2822+ out:
2823+        if (proxy != NULL) {
2824+                g_object_unref (proxy);
2825+        }
2826+        g_free (session_id);
2827+
2828+        return seat_id;
2829+}
2830+
2831+static gboolean
2832+get_uid_from_session_id (GdmUserManager *manager,
2833+                         const char     *session_id,
2834+                         uid_t          *uidp)
2835+{
2836+        DBusGProxy      *proxy;
2837+        GError          *error;
2838+        guint            uid;
2839+        gboolean         res;
2840+
2841+        proxy = dbus_g_proxy_new_for_name (manager->priv->connection,
2842+                                           CK_NAME,
2843+                                           session_id,
2844+                                           CK_SESSION_INTERFACE);
2845+        if (proxy == NULL) {
2846+                g_warning ("Failed to connect to the ConsoleKit session object");
2847+                return FALSE;
2848+        }
2849+
2850+        error = NULL;
2851+        res = dbus_g_proxy_call (proxy,
2852+                                 "GetUnixUser",
2853+                                 &error,
2854+                                 G_TYPE_INVALID,
2855+                                 G_TYPE_UINT, &uid,
2856+                                 G_TYPE_INVALID);
2857+        g_object_unref (proxy);
2858+
2859+        if (! res) {
2860+                g_warning ("Failed to query the session: %s", error->message);
2861+                g_error_free (error);
2862+                return FALSE;
2863+        }
2864+
2865+        if (uidp != NULL) {
2866+                *uidp = (uid_t) uid;
2867+        }
2868+
2869+        return TRUE;
2870+}
2871+
2872+static void
2873+seat_session_added (DBusGProxy     *seat_proxy,
2874+                    const char     *session_id,
2875+                    GdmUserManager *manager)
2876+{
2877+        uid_t          uid;
2878+        gboolean       res;
2879+        struct passwd *pwent;
2880+        GdmUser       *user;
2881+        gboolean       is_new;
2882+
2883+        g_debug ("Session added: %s", session_id);
2884+
2885+        res = get_uid_from_session_id (manager, session_id, &uid);
2886+        if (! res) {
2887+                g_warning ("Unable to lookup user for session");
2888+                return;
2889+        }
2890+
2891+        errno = 0;
2892+        pwent = getpwuid (uid);
2893+        if (pwent == NULL) {
2894+                g_warning ("Unable to lookup user id %d: %s", (int)uid, g_strerror (errno));
2895+                return;
2896+        }
2897+
2898+        /* check exclusions up front */
2899+        if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
2900+                g_debug ("GdmUserManager: excluding user '%s'", pwent->pw_name);
2901+                return;
2902+        }
2903+
2904+        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
2905+        if (user == NULL) {
2906+                g_debug ("Creating new user");
2907+
2908+                user = create_user (manager);
2909+                _gdm_user_update (user, pwent);
2910+                is_new = TRUE;
2911+        } else {
2912+                is_new = FALSE;
2913+        }
2914+
2915+        res = maybe_add_session_for_user (manager, user, session_id);
2916+
2917+        /* only add the user if we added a session */
2918+        if (is_new) {
2919+                if (res) {
2920+                        add_user (manager, user);
2921+                } else {
2922+                        g_object_unref (user);
2923+                }
2924+        }
2925+}
2926+
2927+static void
2928+seat_session_removed (DBusGProxy     *seat_proxy,
2929+                      const char     *session_id,
2930+                      GdmUserManager *manager)
2931+{
2932+        GdmUser *user;
2933+        char    *username;
2934+
2935+        g_debug ("Session removed: %s", session_id);
2936+
2937+        /* since the session object may already be gone
2938+         * we can't query CK directly */
2939+
2940+        username = g_hash_table_lookup (manager->priv->sessions, session_id);
2941+        if (username == NULL) {
2942+                return;
2943+        }
2944+
2945+        user = g_hash_table_lookup (manager->priv->users, username);
2946+        if (user == NULL) {
2947+                /* nothing to do */
2948+                return;
2949+        }
2950+
2951+        g_debug ("GdmUserManager: Session removed for %s", username);
2952+        _gdm_user_remove_session (user, session_id);
2953+}
2954+
2955+static void
2956+on_proxy_destroy (DBusGProxy     *proxy,
2957+                  GdmUserManager *manager)
2958+{
2959+        g_debug ("GdmUserManager: seat proxy destroyed");
2960+
2961+        manager->priv->seat_proxy = NULL;
2962+}
2963+
2964+static void
2965+get_seat_proxy (GdmUserManager *manager)
2966+{
2967+        DBusGProxy      *proxy;
2968+        GError          *error;
2969+
2970+        g_assert (manager->priv->seat_proxy == NULL);
2971+
2972+        error = NULL;
2973+        manager->priv->connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
2974+        if (manager->priv->connection == NULL) {
2975+                g_warning ("Failed to connect to the D-Bus daemon: %s", error->message);
2976+                g_error_free (error);
2977+                return;
2978+        }
2979+
2980+        manager->priv->seat_id = get_current_seat_id (manager->priv->connection);
2981+        if (manager->priv->seat_id == NULL) {
2982+                return;
2983+        }
2984+
2985+        g_debug ("GdmUserManager: Found current seat: %s", manager->priv->seat_id);
2986+
2987+        error = NULL;
2988+        proxy = dbus_g_proxy_new_for_name_owner (manager->priv->connection,
2989+                                                 CK_NAME,
2990+                                                 manager->priv->seat_id,
2991+                                                 CK_SEAT_INTERFACE,
2992+                                                 &error);
2993+
2994+        if (proxy == NULL) {
2995+                g_warning ("Failed to connect to the ConsoleKit seat object: %s", error->message);
2996+                g_error_free (error);
2997+                return;
2998+        }
2999+
3000+        g_signal_connect (proxy, "destroy", G_CALLBACK (on_proxy_destroy), manager);
3001+
3002+        dbus_g_proxy_add_signal (proxy,
3003+                                 "SessionAdded",
3004+                                 DBUS_TYPE_G_OBJECT_PATH,
3005+                                 G_TYPE_INVALID);
3006+        dbus_g_proxy_add_signal (proxy,
3007+                                 "SessionRemoved",
3008+                                 DBUS_TYPE_G_OBJECT_PATH,
3009+                                 G_TYPE_INVALID);
3010+        dbus_g_proxy_connect_signal (proxy,
3011+                                     "SessionAdded",
3012+                                     G_CALLBACK (seat_session_added),
3013+                                     manager,
3014+                                     NULL);
3015+        dbus_g_proxy_connect_signal (proxy,
3016+                                     "SessionRemoved",
3017+                                     G_CALLBACK (seat_session_removed),
3018+                                     manager,
3019+                                     NULL);
3020+        manager->priv->seat_proxy = proxy;
3021+
3022+}
3023+
3024+/**
3025+ * gdm_manager_get_user:
3026+ * @manager: the manager to query.
3027+ * @username: the login name of the user to get.
3028+ *
3029+ * Retrieves a pointer to the #GdmUser object for the login named @username
3030+ * from @manager. This pointer is not a reference, and should not be released.
3031+ *
3032+ * Returns: a pointer to a #GdmUser object.
3033+ **/
3034+GdmUser *
3035+gdm_user_manager_get_user (GdmUserManager *manager,
3036+                           const char     *username)
3037+{
3038+        GdmUser *user;
3039+
3040+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
3041+        g_return_val_if_fail (username != NULL && username[0] != '\0', NULL);
3042+
3043+        user = g_hash_table_lookup (manager->priv->users, username);
3044+
3045+        if (user == NULL) {
3046+                struct passwd *pwent;
3047+
3048+                pwent = getpwnam (username);
3049+
3050+                if (pwent != NULL) {
3051+                        user = add_new_user_for_pwent (manager, pwent);
3052+                }
3053+        }
3054+
3055+        return user;
3056+}
3057+
3058+GdmUser *
3059+gdm_user_manager_get_user_by_uid (GdmUserManager *manager,
3060+                                  uid_t           uid)
3061+{
3062+        GdmUser       *user;
3063+        struct passwd *pwent;
3064+
3065+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
3066+
3067+        pwent = getpwuid (uid);
3068+        if (pwent == NULL) {
3069+                g_warning ("GdmUserManager: unable to lookup uid %d", (int)uid);
3070+                return NULL;
3071+        }
3072+
3073+        user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
3074+
3075+        if (user == NULL) {
3076+                user = add_new_user_for_pwent (manager, pwent);
3077+        }
3078+
3079+        return user;
3080+}
3081+
3082+static void
3083+listify_hash_values_hfunc (gpointer key,
3084+                           gpointer value,
3085+                           gpointer user_data)
3086+{
3087+        GSList **list = user_data;
3088+
3089+        *list = g_slist_prepend (*list, value);
3090+}
3091+
3092+GSList *
3093+gdm_user_manager_list_users (GdmUserManager *manager)
3094+{
3095+        GSList *retval;
3096+
3097+        g_return_val_if_fail (GDM_IS_USER_MANAGER (manager), NULL);
3098+
3099+        retval = NULL;
3100+        g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &retval);
3101+
3102+        return g_slist_sort (retval, (GCompareFunc) gdm_user_collate);
3103+}
3104+
3105+static gboolean
3106+parse_value_as_ulong (const char *value,
3107+                      gulong     *ulongval)
3108+{
3109+        char  *end_of_valid_long;
3110+        glong  long_value;
3111+        gulong ulong_value;
3112+
3113+        errno = 0;
3114+        long_value = strtol (value, &end_of_valid_long, 10);
3115+
3116+        if (*value == '\0' || *end_of_valid_long != '\0') {
3117+                return FALSE;
3118+        }
3119+
3120+        ulong_value = long_value;
3121+        if (ulong_value != long_value || errno == ERANGE) {
3122+                return FALSE;
3123+        }
3124+
3125+        *ulongval = ulong_value;
3126+
3127+        return TRUE;
3128+}
3129+
3130+static gboolean
3131+parse_ck_history_line (const char *line,
3132+                       char      **user_namep,
3133+                       gulong     *frequencyp)
3134+{
3135+        GRegex     *re;
3136+        GMatchInfo *match_info;
3137+        gboolean    res;
3138+        gboolean    ret;
3139+        GError     *error;
3140+
3141+        ret = FALSE;
3142+        re = NULL;
3143+        match_info = NULL;
3144+
3145+        error = NULL;
3146+        re = g_regex_new ("(?P<username>[0-9a-zA-Z]+)[ ]+(?P<frequency>[0-9]+)", 0, 0, &error);
3147+        if (re == NULL) {
3148+                g_critical ("%s", error->message);
3149+                goto out;
3150+        }
3151+
3152+        g_regex_match (re, line, 0, &match_info);
3153+
3154+        res = g_match_info_matches (match_info);
3155+        if (! res) {
3156+                g_warning ("Unable to parse history: %s", line);
3157+                goto out;
3158+        }
3159+
3160+        if (user_namep != NULL) {
3161+                *user_namep = g_match_info_fetch_named (match_info, "username");
3162+        }
3163+
3164+        if (frequencyp != NULL) {
3165+                char *freq;
3166+                freq = g_match_info_fetch_named (match_info, "frequency");
3167+                res = parse_value_as_ulong (freq, frequencyp);
3168+                g_free (freq);
3169+                if (! res) {
3170+                        goto out;
3171+                }
3172+        }
3173+
3174+        ret = TRUE;
3175+
3176+ out:
3177+        if (match_info != NULL) {
3178+                g_match_info_free (match_info);
3179+        }
3180+        if (re != NULL) {
3181+                g_regex_unref (re);
3182+        }
3183+        return ret;
3184+}
3185+
3186+static void
3187+process_ck_history_line (GdmUserManager *manager,
3188+                         const char     *line)
3189+{
3190+        gboolean res;
3191+        char    *username;
3192+        gulong   frequency;
3193+        struct passwd *pwent;
3194+        GdmUser *user;
3195+
3196+        frequency = 0;
3197+        username = NULL;
3198+        res = parse_ck_history_line (line, &username, &frequency);
3199+        if (! res) {
3200+                return;
3201+        }
3202+
3203+        if (g_hash_table_lookup (manager->priv->exclusions, username)) {
3204+                g_debug ("GdmUserManager: excluding user '%s'", username);
3205+                g_free (username);
3206+                return;
3207+        }
3208+
3209+        /* https://bugzilla.gnome.org/show_bug.cgi?id=587708 */
3210+        /* do not show system users; we cannot use gdm_user_manager_get_user()
3211+         * here since this creates/signals users as a side effect */
3212+        pwent = getpwnam (username);
3213+        if (pwent == NULL) {
3214+                g_warning ("Unable to lookup user name %s: %s", username, g_strerror (errno));
3215+                return;
3216+        }
3217+        if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
3218+                g_debug ("GdmUserManager: excluding user '%s'", username);
3219+                return;
3220+        }
3221+
3222+        user = gdm_user_manager_get_user (manager, username);
3223+        if (user == NULL) {
3224+                g_debug ("GdmUserManager: unable to lookup user '%s'", username);
3225+                g_free (username);
3226+                return;
3227+        }
3228+
3229+        g_object_set (user, "login-frequency", frequency, NULL);
3230+        g_signal_emit (manager, signals [USER_LOGIN_FREQUENCY_CHANGED], 0, user);
3231+        g_free (username);
3232+}
3233+
3234+static gboolean
3235+ck_history_watch (GIOChannel     *source,
3236+                  GIOCondition    condition,
3237+                  GdmUserManager *manager)
3238+{
3239+        GIOStatus status;
3240+        gboolean  done  = FALSE;
3241+
3242+        g_return_val_if_fail (manager != NULL, FALSE);
3243+
3244+        if (condition & G_IO_IN) {
3245+                char   *str;
3246+                GError *error;
3247+
3248+                error = NULL;
3249+                status = g_io_channel_read_line (source, &str, NULL, NULL, &error);
3250+                if (error != NULL) {
3251+                        g_warning ("GdmUserManager: unable to read line: %s", error->message);
3252+                        g_error_free (error);
3253+                }
3254+
3255+                if (status == G_IO_STATUS_NORMAL) {
3256+                        g_debug ("GdmUserManager: history output: %s", str);
3257+                        process_ck_history_line (manager, str);
3258+                } else if (status == G_IO_STATUS_EOF) {
3259+                        done = TRUE;
3260+                }
3261+
3262+                g_free (str);
3263+        } else if (condition & G_IO_HUP) {
3264+                done = TRUE;
3265+        }
3266+
3267+        if (done) {
3268+                g_signal_emit (G_OBJECT (manager), signals[USERS_LOADED], 0);
3269+
3270+                manager->priv->ck_history_id = 0;
3271+                return FALSE;
3272+        }
3273+
3274+        return TRUE;
3275+}
3276+
3277+static void
3278+reload_ck_history (GdmUserManager *manager)
3279+{
3280+        char       *command;
3281+        const char *seat_id;
3282+        GError     *error;
3283+        gboolean    res;
3284+        char      **argv;
3285+        int         standard_out;
3286+        GIOChannel *channel;
3287+
3288+        seat_id = NULL;
3289+        if (manager->priv->seat_id != NULL
3290+            && g_str_has_prefix (manager->priv->seat_id, "/org/freedesktop/ConsoleKit/")) {
3291+
3292+                seat_id = manager->priv->seat_id + strlen ("/org/freedesktop/ConsoleKit/");
3293+        }
3294+
3295+        if (seat_id == NULL) {
3296+                g_warning ("Unable to find users: no seat-id found");
3297+                return;
3298+        }
3299+
3300+        command = g_strdup_printf ("ck-history --frequent --seat='%s' --session-type=''",
3301+                                   seat_id);
3302+        g_debug ("GdmUserManager: running '%s'", command);
3303+        error = NULL;
3304+        if (! g_shell_parse_argv (command, NULL, &argv, &error)) {
3305+                g_warning ("Could not parse command: %s", error->message);
3306+                g_error_free (error);
3307+                goto out;
3308+        }
3309+
3310+        error = NULL;
3311+        res = g_spawn_async_with_pipes (NULL,
3312+                                        argv,
3313+                                        NULL,
3314+                                        G_SPAWN_SEARCH_PATH,
3315+                                        NULL,
3316+                                        NULL,
3317+                                        NULL, /* pid */
3318+                                        NULL,
3319+                                        &standard_out,
3320+                                        NULL,
3321+                                        &error);
3322+        g_strfreev (argv);
3323+        if (! res) {
3324+                g_warning ("Unable to run ck-history: %s", error->message);
3325+                g_error_free (error);
3326+                goto out;
3327+        }
3328+
3329+        channel = g_io_channel_unix_new (standard_out);
3330+        g_io_channel_set_close_on_unref (channel, TRUE);
3331+        g_io_channel_set_flags (channel,
3332+                                g_io_channel_get_flags (channel) | G_IO_FLAG_NONBLOCK,
3333+                                NULL);
3334+        manager->priv->ck_history_id = g_io_add_watch (channel,
3335+                                                       G_IO_IN | G_IO_HUP | G_IO_ERR | G_IO_NVAL,
3336+                                                       (GIOFunc)ck_history_watch,
3337+                                                       manager);
3338+        g_io_channel_unref (channel);
3339+
3340+ out:
3341+        g_free (command);
3342+}
3343+
3344+static void
3345+reload_passwd (GdmUserManager *manager)
3346+{
3347+        struct passwd *pwent;
3348+        GSList        *old_users;
3349+        GSList        *new_users;
3350+        GSList        *list;
3351+        FILE          *fp;
3352+
3353+        old_users = NULL;
3354+        new_users = NULL;
3355+
3356+        errno = 0;
3357+        fp = fopen (PATH_PASSWD, "r");
3358+        if (fp == NULL) {
3359+                g_warning ("Unable to open %s: %s", PATH_PASSWD, g_strerror (errno));
3360+                goto out;
3361+        }
3362+
3363+        g_hash_table_foreach (manager->priv->users, listify_hash_values_hfunc, &old_users);
3364+        g_slist_foreach (old_users, (GFunc) g_object_ref, NULL);
3365+
3366+        /* Make sure we keep users who are logged in no matter what. */
3367+        for (list = old_users; list; list = list->next) {
3368+                if (gdm_user_get_num_sessions (list->data) > 0) {
3369+                        g_object_freeze_notify (G_OBJECT (list->data));
3370+                        new_users = g_slist_prepend (new_users, g_object_ref (list->data));
3371+                }
3372+        }
3373+
3374+        for (pwent = fgetpwent (fp); pwent != NULL; pwent = fgetpwent (fp)) {
3375+                GdmUser *user;
3376+
3377+                user = NULL;
3378+
3379+                /* Skip users below MinimalUID... */
3380+                if (pwent->pw_uid < DEFAULT_MINIMAL_UID) {
3381+                        continue;
3382+                }
3383+
3384+                /* ...And users w/ invalid shells... */
3385+                if (pwent->pw_shell == NULL ||
3386+                    !g_hash_table_lookup (manager->priv->shells, pwent->pw_shell)) {
3387+                        g_debug ("GdmUserManager: skipping user with bad shell: %s", pwent->pw_name);
3388+                        continue;
3389+                }
3390+
3391+                /* ...And explicitly excluded users */
3392+                if (g_hash_table_lookup (manager->priv->exclusions, pwent->pw_name)) {
3393+                        g_debug ("GdmUserManager: explicitly skipping user: %s", pwent->pw_name);
3394+                        continue;
3395+                }
3396+
3397+                user = g_hash_table_lookup (manager->priv->users, pwent->pw_name);
3398+
3399+                /* Update users already in the *new* list */
3400+                if (g_slist_find (new_users, user)) {
3401+                        _gdm_user_update (user, pwent);
3402+                        continue;
3403+                }
3404+
3405+                if (user == NULL) {
3406+                        user = create_user (manager);
3407+                } else {
3408+                        g_object_ref (user);
3409+                }
3410+
3411+                /* Freeze & update users not already in the new list */
3412+                g_object_freeze_notify (G_OBJECT (user));
3413+                _gdm_user_update (user, pwent);
3414+
3415+                new_users = g_slist_prepend (new_users, user);
3416+        }
3417+
3418+        /* Go through and handle added users */
3419+        for (list = new_users; list; list = list->next) {
3420+                if (! g_slist_find (old_users, list->data)) {
3421+                        add_user (manager, list->data);
3422+                }
3423+        }
3424+
3425+        /* Go through and handle removed users */
3426+        for (list = old_users; list; list = list->next) {
3427+                if (! g_slist_find (new_users, list->data)) {
3428+                        g_signal_emit (manager, signals[USER_REMOVED], 0, list->data);
3429+                        g_hash_table_remove (manager->priv->users,
3430+                                             gdm_user_get_user_name (list->data));
3431+                }
3432+        }
3433+
3434+ out:
3435+        /* Cleanup */
3436+
3437+        fclose (fp);
3438+
3439+        g_slist_foreach (new_users, (GFunc) g_object_thaw_notify, NULL);
3440+        g_slist_foreach (new_users, (GFunc) g_object_unref, NULL);
3441+        g_slist_free (new_users);
3442+
3443+        g_slist_foreach (old_users, (GFunc) g_object_unref, NULL);
3444+        g_slist_free (old_users);
3445+}
3446+
3447+static void
3448+reload_users (GdmUserManager *manager)
3449+{
3450+        reload_ck_history (manager);
3451+        reload_passwd (manager);
3452+}
3453+
3454+static gboolean
3455+reload_users_timeout (GdmUserManager *manager)
3456+{
3457+        reload_users (manager);
3458+        manager->priv->reload_id = 0;
3459+
3460+        return FALSE;
3461+}
3462+
3463+static void
3464+queue_reload_users (GdmUserManager *manager)
3465+{
3466+        if (manager->priv->reload_id > 0) {
3467+                return;
3468+        }
3469+
3470+        g_signal_emit (G_OBJECT (manager), signals[LOADING_USERS], 0);
3471+        manager->priv->reload_id = g_idle_add ((GSourceFunc)reload_users_timeout, manager);
3472+}
3473+
3474+static void
3475+reload_shells (GdmUserManager *manager)
3476+{
3477+        char *shell;
3478+
3479+        setusershell ();
3480+
3481+        g_hash_table_remove_all (manager->priv->shells);
3482+        for (shell = getusershell (); shell != NULL; shell = getusershell ()) {
3483+                /* skip well known not-real shells */
3484+                if (shell == NULL
3485+                    || strcmp (shell, "/sbin/nologin") == 0
3486+                    || strcmp (shell, "/bin/false") == 0) {
3487+                        g_debug ("GdmUserManager: skipping shell %s", shell);
3488+                        continue;
3489+                }
3490+                g_hash_table_insert (manager->priv->shells,
3491+                                     g_strdup (shell),
3492+                                     GUINT_TO_POINTER (TRUE));
3493+        }
3494+
3495+        endusershell ();
3496+}
3497+
3498+static void
3499+on_shells_monitor_changed (GFileMonitor     *monitor,
3500+                           GFile            *file,
3501+                           GFile            *other_file,
3502+                           GFileMonitorEvent event_type,
3503+                           GdmUserManager   *manager)
3504+{
3505+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
3506+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
3507+                return;
3508+        }
3509+
3510+        reload_shells (manager);
3511+        reload_passwd (manager);
3512+}
3513+
3514+static void
3515+on_passwd_monitor_changed (GFileMonitor     *monitor,
3516+                           GFile            *file,
3517+                           GFile            *other_file,
3518+                           GFileMonitorEvent event_type,
3519+                           GdmUserManager   *manager)
3520+{
3521+        if (event_type != G_FILE_MONITOR_EVENT_CHANGED &&
3522+            event_type != G_FILE_MONITOR_EVENT_CREATED) {
3523+                return;
3524+        }
3525+
3526+        reload_passwd (manager);
3527+}
3528+
3529+static void
3530+gdm_user_manager_class_init (GdmUserManagerClass *klass)
3531+{
3532+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
3533+
3534+        object_class->finalize = gdm_user_manager_finalize;
3535+
3536+        signals [LOADING_USERS] =
3537+                g_signal_new ("loading-users",
3538+                              G_TYPE_FROM_CLASS (klass),
3539+                              G_SIGNAL_RUN_LAST,
3540+                              G_STRUCT_OFFSET (GdmUserManagerClass, loading_users),
3541+                              NULL, NULL,
3542+                              g_cclosure_marshal_VOID__VOID,
3543+                              G_TYPE_NONE, 0);
3544+        signals [USERS_LOADED] =
3545+                g_signal_new ("users-loaded",
3546+                              G_TYPE_FROM_CLASS (klass),
3547+                              G_SIGNAL_RUN_LAST,
3548+                              G_STRUCT_OFFSET (GdmUserManagerClass, users_loaded),
3549+                              NULL, NULL,
3550+                              g_cclosure_marshal_VOID__VOID,
3551+                              G_TYPE_NONE, 0);
3552+        signals [USER_ADDED] =
3553+                g_signal_new ("user-added",
3554+                              G_TYPE_FROM_CLASS (klass),
3555+                              G_SIGNAL_RUN_LAST,
3556+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_added),
3557+                              NULL, NULL,
3558+                              g_cclosure_marshal_VOID__OBJECT,
3559+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
3560+        signals [USER_REMOVED] =
3561+                g_signal_new ("user-removed",
3562+                              G_TYPE_FROM_CLASS (klass),
3563+                              G_SIGNAL_RUN_LAST,
3564+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_removed),
3565+                              NULL, NULL,
3566+                              g_cclosure_marshal_VOID__OBJECT,
3567+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
3568+        signals [USER_IS_LOGGED_IN_CHANGED] =
3569+                g_signal_new ("user-is-logged-in-changed",
3570+                              G_TYPE_FROM_CLASS (klass),
3571+                              G_SIGNAL_RUN_LAST,
3572+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_is_logged_in_changed),
3573+                              NULL, NULL,
3574+                              g_cclosure_marshal_VOID__OBJECT,
3575+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
3576+        signals [USER_LOGIN_FREQUENCY_CHANGED] =
3577+                g_signal_new ("user-login-frequency-changed",
3578+                              G_TYPE_FROM_CLASS (klass),
3579+                              G_SIGNAL_RUN_LAST,
3580+                              G_STRUCT_OFFSET (GdmUserManagerClass, user_login_frequency_changed),
3581+                              NULL, NULL,
3582+                              g_cclosure_marshal_VOID__OBJECT,
3583+                              G_TYPE_NONE, 1, GDM_TYPE_USER);
3584+
3585+        g_type_class_add_private (klass, sizeof (GdmUserManagerPrivate));
3586+}
3587+
3588+static void
3589+gdm_user_manager_init (GdmUserManager *manager)
3590+{
3591+        int            i;
3592+        GFile         *file;
3593+        GError        *error;
3594+        const char    *exclude_default[] = DEFAULT_EXCLUDE;
3595+
3596+        manager->priv = GDM_USER_MANAGER_GET_PRIVATE (manager);
3597+
3598+        /* sessions */
3599+        manager->priv->sessions = g_hash_table_new_full (g_str_hash,
3600+                                                         g_str_equal,
3601+                                                         g_free,
3602+                                                         g_free);
3603+
3604+        /* exclusions */
3605+        manager->priv->exclusions = g_hash_table_new_full (g_str_hash,
3606+                                                           g_str_equal,
3607+                                                           g_free,
3608+                                                           NULL);
3609+        for (i = 0; exclude_default[i] != NULL; i++) {
3610+                g_hash_table_insert (manager->priv->exclusions,
3611+                                     g_strdup (exclude_default [i]),
3612+                                     GUINT_TO_POINTER (TRUE));
3613+        }
3614+
3615+        /* /etc/shells */
3616+        manager->priv->shells = g_hash_table_new_full (g_str_hash,
3617+                                                       g_str_equal,
3618+                                                       g_free,
3619+                                                       NULL);
3620+        reload_shells (manager);
3621+        file = g_file_new_for_path (_PATH_SHELLS);
3622+        error = NULL;
3623+        manager->priv->shells_monitor = g_file_monitor_file (file,
3624+                                                             G_FILE_MONITOR_NONE,
3625+                                                             NULL,
3626+                                                             &error);
3627+        if (manager->priv->shells_monitor != NULL) {
3628+                g_signal_connect (manager->priv->shells_monitor,
3629+                                  "changed",
3630+                                  G_CALLBACK (on_shells_monitor_changed),
3631+                                  manager);
3632+        } else {
3633+                g_warning ("Unable to monitor %s: %s", _PATH_SHELLS, error->message);
3634+                g_error_free (error);
3635+        }
3636+        g_object_unref (file);
3637+
3638+        /* /etc/passwd */
3639+        manager->priv->users = g_hash_table_new_full (g_str_hash,
3640+                                                      g_str_equal,
3641+                                                      g_free,
3642+                                                      (GDestroyNotify) g_object_run_dispose);
3643+        file = g_file_new_for_path (PATH_PASSWD);
3644+        manager->priv->passwd_monitor = g_file_monitor_file (file,
3645+                                                             G_FILE_MONITOR_NONE,
3646+                                                             NULL,
3647+                                                             &error);
3648+        if (manager->priv->passwd_monitor != NULL) {
3649+                g_signal_connect (manager->priv->passwd_monitor,
3650+                                  "changed",
3651+                                  G_CALLBACK (on_passwd_monitor_changed),
3652+                                  manager);
3653+        } else {
3654+                g_warning ("Unable to monitor %s: %s", PATH_PASSWD, error->message);
3655+                g_error_free (error);
3656+        }
3657+        g_object_unref (file);
3658+
3659+
3660+        get_seat_proxy (manager);
3661+
3662+        queue_reload_users (manager);
3663+
3664+        manager->priv->users_dirty = FALSE;
3665+}
3666+
3667+static void
3668+gdm_user_manager_finalize (GObject *object)
3669+{
3670+        GdmUserManager *manager;
3671+
3672+        g_return_if_fail (object != NULL);
3673+        g_return_if_fail (GDM_IS_USER_MANAGER (object));
3674+
3675+        manager = GDM_USER_MANAGER (object);
3676+
3677+        g_return_if_fail (manager->priv != NULL);
3678+
3679+        if (manager->priv->seat_proxy != NULL) {
3680+                g_object_unref (manager->priv->seat_proxy);
3681+        }
3682+
3683+        if (manager->priv->ck_history_id != 0) {
3684+                g_source_remove (manager->priv->ck_history_id);
3685+                manager->priv->ck_history_id = 0;
3686+        }
3687+
3688+        if (manager->priv->reload_id > 0) {
3689+                g_source_remove (manager->priv->reload_id);
3690+                manager->priv->reload_id = 0;
3691+        }
3692+
3693+        g_hash_table_destroy (manager->priv->sessions);
3694+
3695+        g_file_monitor_cancel (manager->priv->passwd_monitor);
3696+        g_hash_table_destroy (manager->priv->users);
3697+
3698+        g_file_monitor_cancel (manager->priv->shells_monitor);
3699+        g_hash_table_destroy (manager->priv->shells);
3700+
3701+        g_free (manager->priv->seat_id);
3702+
3703+        G_OBJECT_CLASS (gdm_user_manager_parent_class)->finalize (object);
3704+}
3705+
3706+GdmUserManager *
3707+gdm_user_manager_ref_default (void)
3708+{
3709+        if (user_manager_object != NULL) {
3710+                g_object_ref (user_manager_object);
3711+        } else {
3712+                user_manager_object = g_object_new (GDM_TYPE_USER_MANAGER, NULL);
3713+                g_object_add_weak_pointer (user_manager_object,
3714+                                           (gpointer *) &user_manager_object);
3715+        }
3716+
3717+        return GDM_USER_MANAGER (user_manager_object);
3718+}
3719diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdm-user-manager.h gdm-2.28.0.new/gui/gdmsetup/gdm-user-manager.h
3720--- gdm-2.28.0/gui/gdmsetup/gdm-user-manager.h  1970-01-01 01:00:00.000000000 +0100
3721+++ gdm-2.28.0.new/gui/gdmsetup/gdm-user-manager.h      2009-09-22 16:42:25.000000000 +0200
3722@@ -0,0 +1,87 @@
3723+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3724+ *
3725+ * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
3726+ *
3727+ * This program is free software; you can redistribute it and/or modify
3728+ * it under the terms of the GNU General Public License as published by
3729+ * the Free Software Foundation; either version 2 of the License, or
3730+ * (at your option) any later version.
3731+ *
3732+ * This program is distributed in the hope that it will be useful,
3733+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3734+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3735+ * GNU General Public License for more details.
3736+ *
3737+ * You should have received a copy of the GNU General Public License
3738+ * along with this program; if not, write to the Free Software
3739+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
3740+ *
3741+ */
3742+
3743+#ifndef __GDM_USER_MANAGER_H
3744+#define __GDM_USER_MANAGER_H
3745+
3746+#include <glib-object.h>
3747+
3748+#include "gdm-user.h"
3749+
3750+G_BEGIN_DECLS
3751+
3752+#define GDM_TYPE_USER_MANAGER         (gdm_user_manager_get_type ())
3753+#define GDM_USER_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_USER_MANAGER, GdmUserManager))
3754+#define GDM_USER_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
3755+#define GDM_IS_USER_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_USER_MANAGER))
3756+#define GDM_IS_USER_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_USER_MANAGER))
3757+#define GDM_USER_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_USER_MANAGER, GdmUserManagerClass))
3758+
3759+typedef struct GdmUserManagerPrivate GdmUserManagerPrivate;
3760+
3761+typedef struct
3762+{
3763+        GObject                parent;
3764+        GdmUserManagerPrivate *priv;
3765+} GdmUserManager;
3766+
3767+typedef struct
3768+{
3769+        GObjectClass   parent_class;
3770+
3771+        void          (* loading_users)             (GdmUserManager *user_manager);
3772+        void          (* users_loaded)              (GdmUserManager *user_manager);
3773+        void          (* user_added)                (GdmUserManager *user_manager,
3774+                                                     GdmUser        *user);
3775+        void          (* user_removed)              (GdmUserManager *user_manager,
3776+                                                     GdmUser        *user);
3777+        void          (* user_is_logged_in_changed) (GdmUserManager *user_manager,
3778+                                                     GdmUser        *user);
3779+        void          (* user_login_frequency_changed) (GdmUserManager *user_manager,
3780+                                                        GdmUser        *user);
3781+} GdmUserManagerClass;
3782+
3783+typedef enum
3784+{
3785+        GDM_USER_MANAGER_ERROR_GENERAL,
3786+        GDM_USER_MANAGER_ERROR_KEY_NOT_FOUND
3787+} GdmUserManagerError;
3788+
3789+#define GDM_USER_MANAGER_ERROR gdm_user_manager_error_quark ()
3790+
3791+GQuark              gdm_user_manager_error_quark           (void);
3792+GType               gdm_user_manager_get_type              (void);
3793+
3794+GdmUserManager *    gdm_user_manager_ref_default           (void);
3795+
3796+GSList *            gdm_user_manager_list_users            (GdmUserManager *manager);
3797+GdmUser *           gdm_user_manager_get_user              (GdmUserManager *manager,
3798+                                                            const char     *user_name);
3799+GdmUser *           gdm_user_manager_get_user_by_uid       (GdmUserManager *manager,
3800+                                                            uid_t           uid);
3801+
3802+gboolean            gdm_user_manager_activate_user_session (GdmUserManager *manager,
3803+                                                            GdmUser        *user);
3804+
3805+gboolean            gdm_user_manager_goto_login_session    (GdmUserManager *manager);
3806+
3807+G_END_DECLS
3808+
3809+#endif /* __GDM_USER_MANAGER_H */
3810diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/gdm-user-private.h gdm-2.28.0.new/gui/gdmsetup/gdm-user-private.h
3811--- gdm-2.28.0/gui/gdmsetup/gdm-user-private.h  1970-01-01 01:00:00.000000000 +0100
3812+++ gdm-2.28.0.new/gui/gdmsetup/gdm-user-private.h      2009-09-22 16:42:25.000000000 +0200
3813@@ -0,0 +1,44 @@
3814+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
3815+ *
3816+ * Copyright (C) 2004-2005 James M. Cape <jcape@ignore-your.tv>.
3817+ *
3818+ * This program is free software; you can redistribute it and/or modify
3819+ * it under the terms of the GNU General Public License as published by
3820+ * the Free Software Foundation; either version 2 of the License, or
3821+ * (at your option) any later version.
3822+ *
3823+ * This program is distributed in the hope that it will be useful,
3824+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3825+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
3826+ * GNU General Public License for more details.
3827+ *
3828+ * You should have received a copy of the GNU General Public License
3829+ * along with this program; if not, write to the Free Software
3830+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
3831+ */
3832+
3833+/*
3834+ * Private interfaces to the GdmUser object
3835+ */
3836+
3837+#ifndef __GDM_USER_PRIVATE__
3838+#define __GDM_USER_PRIVATE__ 1
3839+
3840+#include <pwd.h>
3841+
3842+#include "gdm-user.h"
3843+
3844+G_BEGIN_DECLS
3845+
3846+void _gdm_user_update           (GdmUser             *user,
3847+                                 const struct passwd *pwent);
3848+void _gdm_user_add_session      (GdmUser             *user,
3849+                                 const char          *session_id);
3850+void _gdm_user_remove_session   (GdmUser             *user,
3851+                                 const char          *session_id);
3852+
3853+void _gdm_user_icon_changed     (GdmUser             *user);
3854+
3855+G_END_DECLS
3856+
3857+#endif /* !__GDM_USER_PRIVATE__ */
3858diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/gdmsetup/Makefile.am gdm-2.28.0.new/gui/gdmsetup/Makefile.am
3859--- gdm-2.28.0/gui/gdmsetup/Makefile.am 1970-01-01 01:00:00.000000000 +0100
3860+++ gdm-2.28.0.new/gui/gdmsetup/Makefile.am     2009-09-22 16:42:25.000000000 +0200
3861@@ -0,0 +1,38 @@
3862+NULL =
3863+
3864+AM_CPPFLAGS = \
3865+       -I$(top_srcdir)/common          \
3866+       -DDATADIR=\""$(datadir)"\"      \
3867+       -DUIDIR=\""$(uidir)"\"  \
3868+       -DGNOMELOCALEDIR=\""$(gdmlocaledir)"\"  \
3869+       $(GDMSETUP_CFLAGS)              \
3870+       $(NULL)
3871+
3872+bin_PROGRAMS =                 \
3873+       gdmsetup                \
3874+       $(NULL)
3875+
3876+gdmsetup_SOURCES =             \
3877+       gdmsetup.c              \
3878+       gdm-user.c              \
3879+       gdm-user-manager.c      \
3880+       $(NULL)
3881+
3882+gdmsetup_LDADD =               \
3883+       $(GDMSETUP_LIBS)        \
3884+       $(NULL)
3885+       
3886+uidir = $(pkgdatadir)
3887+ui_DATA =                      \
3888+       gdmsetup.ui             \
3889+       $(NULL)
3890+
3891+Utilitiesdir = $(datadir)/applications
3892+Utilities_in_files = gdmsetup.desktop.in
3893+Utilities_DATA = $(Utilities_in_files:.desktop.in=.desktop)
3894+@INTLTOOL_DESKTOP_RULE@
3895+
3896+EXTRA_DIST =                   \
3897+       $(ui_DATA)              \
3898+       $(Utilities_in_files)   \
3899+       $(NULL)
3900diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/gui/Makefile.am gdm-2.28.0.new/gui/Makefile.am
3901--- gdm-2.28.0/gui/Makefile.am  2009-09-21 22:05:27.000000000 +0200
3902+++ gdm-2.28.0.new/gui/Makefile.am      2009-09-22 16:42:25.000000000 +0200
3903@@ -1,6 +1,7 @@
3904 NULL =
3905 
3906 SUBDIRS =                      \
3907+       gdmsetup                \
3908        simple-chooser          \
3909        simple-greeter          \
3910        user-switch-applet      \
3911diff -Nur -x '*.orig' -x '*~' gdm-2.28.0/po/POTFILES.in gdm-2.28.0.new/po/POTFILES.in
3912--- gdm-2.28.0/po/POTFILES.in   2009-09-22 16:42:24.000000000 +0200
3913+++ gdm-2.28.0.new/po/POTFILES.in       2009-09-22 16:42:25.000000000 +0200
3914@@ -60,6 +60,9 @@
3915 data/greeter-autostart/metacity.desktop.in
3916 data/greeter-autostart/orca-screen-reader.desktop.in
3917 data/greeter-autostart/polkit-gnome-authentication-agent-1.desktop.in
3918+gui/gdmsetup/gdmsetup.desktop.in
3919+gui/gdmsetup/gdmsetup.c
3920+[type: gettext/glade]gui/gdmsetup/gdmsetup.ui
3921 gui/simple-chooser/gdm-host-chooser-dialog.c
3922 gui/simple-chooser/gdm-host-chooser-widget.c
3923 gui/simple-greeter/gdm-cell-renderer-timer.c
Note: See TracBrowser for help on using the repository browser.