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