#
# Description: Choose default session in /etc/gdm/custom.conf using DBus
# Add /usr/lib/gdm-set-default-session to properly define default session:
# Use DBus when gdm daemon is on and filing the file when it's off
# Ubuntu: https://launchpad.net/bugs/403291
# Upstream: https://bugzilla.gnome.org/show_bug.cgi?id=594733
#
diff -Nur -x '*.orig' -x '*~' gdm-2.29.5/configure.ac gdm-2.29.5.new/configure.ac
--- gdm-2.29.5/configure.ac	2010-01-19 09:51:38.222451637 +0100
+++ gdm-2.29.5.new/configure.ac	2010-01-19 09:51:39.489451835 +0100
@@ -126,6 +126,13 @@
 AC_SUBST(GDMSETUP_CFLAGS)
 AC_SUBST(GDMSETUP_LIBS)
 
+PKG_CHECK_MODULES(SET_SESSION,
+        dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
+        gobject-2.0 >= $GLIB_REQUIRED_VERSION
+)
+AC_SUBST(SET_SESSION_CFLAGS)
+AC_SUBST(SET_SESSION_LIBS)
+
 PKG_CHECK_MODULES(SIMPLE_GREETER,
         dbus-glib-1 >= $DBUS_GLIB_REQUIRED_VERSION
         gtk+-2.0 >= $GTK_REQUIRED_VERSION
diff -Nur -x '*.orig' -x '*~' gdm-2.29.5/daemon/gdm-session-direct.c gdm-2.29.5.new/daemon/gdm-session-direct.c
--- gdm-2.29.5/daemon/gdm-session-direct.c	2010-01-19 09:51:37.853462132 +0100
+++ gdm-2.29.5.new/daemon/gdm-session-direct.c	2010-01-19 09:51:39.489451835 +0100
@@ -60,6 +60,10 @@
 #define GDM_SESSION_DBUS_INTERFACE    "org.gnome.DisplayManager.Session"
 #define GDM_SESSION_DBUS_ERROR_CANCEL "org.gnome.DisplayManager.Session.Error.Cancel"
 
+#define GDM_SETTINGS_DBUS_NAME "org.gnome.DisplayManager"
+#define GDM_SETTINGS_DBUS_PATH "/org/gnome/DisplayManager/Settings"
+#define GDM_SETTINGS_DBUS_INTERFACE "org.gnome.DisplayManager.Settings"
+
 #ifndef GDM_SESSION_DEFAULT_PATH
 #define GDM_SESSION_DEFAULT_PATH "/usr/local/bin:/usr/bin:/bin"
 #endif
@@ -640,11 +644,31 @@
 }
 
 static char *
-get_fallback_session_name (void)
+get_fallback_session_name (GdmSessionDirect *session)
 {
         const char  **search_dirs;
         int           i;
         char         *name;
+        DBusGProxy *proxy = NULL;
+        GError *error = NULL;
+
+        proxy = dbus_g_proxy_new_for_name (session->priv->connection,
+                                       GDM_SETTINGS_DBUS_NAME,
+                                       GDM_SETTINGS_DBUS_PATH,
+                                       GDM_SETTINGS_DBUS_INTERFACE);
+        if (dbus_g_proxy_call (proxy, "GetValue", &error,
+                                G_TYPE_STRING, "daemon/DefaultSession", G_TYPE_INVALID,
+                                G_TYPE_STRING, &name, G_TYPE_INVALID)) {
+               if (get_session_command_for_name (name, NULL)) {
+                        return name;
+               }
+        }
+        else {
+               g_debug ("No DefaultSession: %s", error->message);
+               g_error_free (error);
+        }
+        if (proxy)
+               g_object_unref (proxy);
 
         name = g_strdup ("gnome");
         if (get_session_command_for_name (name, NULL)) {
@@ -696,7 +720,7 @@
                 return session->priv->saved_session;
         }
 
-        return get_fallback_session_name ();
+        return get_fallback_session_name (session);
 }
 
 static void
diff -Nur -x '*.orig' -x '*~' gdm-2.29.5/utils/gdm-set-default-session.c gdm-2.29.5.new/utils/gdm-set-default-session.c
--- gdm-2.29.5/utils/gdm-set-default-session.c	1970-01-01 01:00:00.000000000 +0100
+++ gdm-2.29.5.new/utils/gdm-set-default-session.c	2010-01-19 09:54:39.274255062 +0100
@@ -0,0 +1,271 @@
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus-glib-bindings.h>
+
+#define GDM_SETTINGS_DBUS_NAME "org.gnome.DisplayManager"
+#define GDM_SETTINGS_DBUS_PATH "/org/gnome/DisplayManager/Settings"
+#define GDM_SETTINGS_DBUS_INTERFACE "org.gnome.DisplayManager.Settings"
+
+#define SESSION_KEY_GROUP "daemon"
+#define SESSION_KEY_NAME  "DefaultSession"
+
+typedef enum {
+    CONNEXIONSUCCEED,
+    CONNEXIONFAILED,
+    ALREADYHASVALUE,
+    HASNOVALUE,
+    VALUEFOUND
+} DBusState;
+
+static gboolean debug = FALSE;
+static gboolean keepold = FALSE;
+static gboolean remove = FALSE;
+
+static GOptionEntry entries[] =
+{
+  { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Enable debugging", NULL },
+  { "keep-old", 'k', 0, G_OPTION_ARG_NONE, &keepold, "Only update if no default already set", NULL },
+  { "remove", 'r', 0, G_OPTION_ARG_NONE, &remove, "Remove default session if it's this one", NULL },
+  { NULL }
+};
+
+void
+show_nothing(const gchar   *log_domain,
+             GLogLevelFlags log_level,
+             const gchar   *message,
+             gpointer       unused_data) {};
+
+int
+init_dbus_connection(DBusGProxy **proxy) {
+    DBusGConnection *connection;
+    GError          *error = NULL;
+
+    connection = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
+    if (connection == NULL) {
+         g_debug ("Can't connect to system bus: %s", error->message);
+         g_error_free (error);
+         return(CONNEXIONFAILED);
+    }
+
+    *proxy = dbus_g_proxy_new_for_name_owner (connection,
+                                              GDM_SETTINGS_DBUS_NAME,
+                                              GDM_SETTINGS_DBUS_PATH,
+                                              GDM_SETTINGS_DBUS_INTERFACE,
+                                              &error);
+    if(!*proxy) {
+         g_debug ("No object on the bus: %s", error->message);
+         g_error_free (error);
+         return(CONNEXIONFAILED);
+    }
+
+    return(CONNEXIONSUCCEED);
+}
+
+int
+get_default_session_name_with_dbus(DBusGProxy *proxy, gchar **name)
+{
+    GError *error = NULL;
+
+    if (!dbus_g_proxy_call (proxy, "GetValue", &error,
+                            G_TYPE_STRING, SESSION_KEY_GROUP "/" SESSION_KEY_NAME, G_TYPE_INVALID,
+                            G_TYPE_STRING, name, G_TYPE_INVALID)) {
+        // This probably (_owner used previously) means that the value doesn't exist in config file
+        if(error->domain == DBUS_GERROR && error->code == DBUS_GERROR_REMOTE_EXCEPTION) {
+            g_debug ("Probably no value registered: %s. %s", dbus_g_error_get_name (error), error->message);
+            g_error_free (error);
+            return(HASNOVALUE);
+        }
+        // possible if GDM_SETTINGS_DBUS_PATH or GDM_SETTINGS_DBUS_INTERFACE aren't exposed by the
+        // existing GDM_SETTINGS_DBUS_NAME (shouldn't happen)
+        else {
+            g_debug ("No GDM_SETTINGS_DBUS_PATH or GDM_SETTINGS_DBUS_INTERFACE on the bus: %s", error->message);
+            g_error_free (error);
+            return(CONNEXIONFAILED);
+        }
+    }
+    return(VALUEFOUND);
+
+}
+
+int
+set_default_session_name_with_dbus(DBusGProxy *proxy, gchar *sessionname)
+{
+    GError *error = NULL;
+    
+    dbus_g_proxy_call (proxy, "SetValue", &error,
+                       G_TYPE_STRING, SESSION_KEY_GROUP "/" SESSION_KEY_NAME,
+                       G_TYPE_STRING, sessionname, G_TYPE_INVALID, G_TYPE_INVALID);
+    if (error) {
+        g_debug ("Error changing default session value to '%s': %s\nNo update will be done", sessionname, error->message);
+        g_error_free (error);
+        return FALSE;
+    }
+    
+    return TRUE;
+}
+
+int
+update_session_if_needed(gchar *default_session, gchar *proposed_session, gboolean dbusupdate, gpointer *parameter)
+{
+    DBusGProxy      *proxy = NULL;
+    GKeyFile        *keyfile = NULL;
+    gboolean         success = FALSE;
+
+    if (dbusupdate)
+        proxy = (DBusGProxy *) parameter;
+    else {
+        keyfile = (GKeyFile *) parameter;
+        success = TRUE; // by default, the function succeed (return void)
+    }
+        
+    if (!(default_session)) {
+        g_debug("No value previously set. Update to %s", proposed_session);
+        if (dbusupdate)
+            success = set_default_session_name_with_dbus(proxy, proposed_session);
+        else
+            g_key_file_set_string (keyfile, SESSION_KEY_GROUP, SESSION_KEY_NAME, proposed_session);
+    }
+    else {
+        if (remove) {
+            if (g_strcmp0(proposed_session, default_session) == 0) {
+                g_debug("Remove %s as default session", proposed_session);
+                if (dbusupdate)
+                    success = set_default_session_name_with_dbus(proxy, "");
+                else
+                    g_key_file_set_string (keyfile, SESSION_KEY_GROUP, SESSION_KEY_NAME, "");
+                if (!success)
+                    return(2);
+                return(0);
+            }
+            g_debug("Don't remove: %s not default session", proposed_session);
+            return(4);
+        }
+        if (strlen(default_session) < 1) {
+            g_debug("Empty value set as gdm default session. Set to %s", proposed_session);
+            if (dbusupdate)
+                success = set_default_session_name_with_dbus(proxy, proposed_session);
+            else
+                g_key_file_set_string (keyfile, SESSION_KEY_GROUP, SESSION_KEY_NAME, proposed_session);
+        }
+        else {
+            g_debug("Found existing default session: %s", default_session);
+            if(keepold)
+                g_debug("keep-old mode: keep previous default session");
+            else {
+                g_debug("Update to %s", proposed_session);
+                if (dbusupdate)
+                    success = set_default_session_name_with_dbus(proxy, proposed_session);
+                else
+                    g_key_file_set_string (keyfile, SESSION_KEY_GROUP, SESSION_KEY_NAME, proposed_session);
+            }
+        }
+    }
+    if (!success)
+        return(2);
+    return(0);
+}
+
+int 
+main (int argc, char *argv[])
+{
+    GOptionContext *context = NULL;
+    GError         *error = NULL;
+
+    DBusGProxy     *proxy = NULL;
+    DBusState       return_dbus_code = CONNEXIONFAILED;
+    gboolean        dbus_connexion_ok = FALSE;
+
+    GKeyFile       *keyfile;
+    GKeyFileFlags   flags;
+    gchar          *s_data;
+    gsize           size;
+    const gchar    *gdm_conf_file = GDMCONFDIR "/custom.conf";
+
+    gchar          *default_session = NULL;
+    gchar          *proposed_session = NULL;
+    gint            return_code;
+
+    g_type_init ();
+
+    context = g_option_context_new (_("- set gdm default session"));
+    g_option_context_add_main_entries (context, entries, GETTEXT_PACKAGE);
+    if (!g_option_context_parse (context, &argc, &argv, &error)) {
+        g_printerr (_("option parsing failed: %s\n"), error->message);
+        g_option_context_free(context);
+        g_error_free (error);
+        exit (1);
+    }
+    if (argc!=2) {
+        g_printerr(_("Wrong usage of the command\n%s"), g_option_context_get_help (context, FALSE, NULL));
+        g_option_context_free(context); 
+        exit(1);
+    }
+    if (context)
+        g_option_context_free(context); 
+    if (!debug)
+        g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, show_nothing, NULL);
+    proposed_session = argv[1];
+
+
+    if (init_dbus_connection(&proxy) == CONNEXIONSUCCEED) {
+        return_dbus_code = get_default_session_name_with_dbus(proxy, &default_session);
+        if (return_dbus_code == CONNEXIONFAILED)
+            dbus_connexion_ok = FALSE; // dbus and service connexion ok, but can't access proxy
+        else {
+            dbus_connexion_ok = TRUE;
+            if (return_dbus_code == HASNOVALUE)
+                default_session = NULL;
+            return_code = update_session_if_needed (default_session, proposed_session, TRUE, (gpointer *) proxy);
+        }
+    }
+    if (proxy)
+       g_object_unref (proxy);
+
+    if (!dbus_connexion_ok) {
+        g_debug ("Can't change value by dbus, failback in %s direct modification", gdm_conf_file);
+        if (geteuid() != 0) {
+            g_printerr ("Updating directly %s requires root permission\n", gdm_conf_file);
+            exit(1);
+        }
+        keyfile = g_key_file_new ();
+        flags = G_KEY_FILE_KEEP_COMMENTS | G_KEY_FILE_KEEP_TRANSLATIONS;
+        if (!(g_key_file_load_from_file (keyfile, gdm_conf_file, flags, &error))) {
+            g_debug ("File doesn't seem to exist or can't be read: create one (%s)", error->message);
+            g_error_free (error);
+            error = NULL;
+        }
+        // try to get the right key
+        default_session = g_key_file_get_string (keyfile, SESSION_KEY_GROUP, SESSION_KEY_NAME, NULL);
+        return_code = update_session_if_needed (default_session, proposed_session, FALSE, (gpointer *) keyfile);
+        if(return_code == 0) {
+            s_data = g_key_file_to_data (keyfile, &size, &error);
+            if (!s_data) {
+                g_debug ("Can't convert data to string: %s", error->message);
+                g_error_free (error);
+                return_code = 1;
+            }
+            else {
+                if(!g_file_set_contents (gdm_conf_file, s_data, size, &error)) {
+                    g_printerr ("Can't update: %s\n", error->message);
+                    g_error_free (error);
+                    return_code = 1;
+                }
+                g_free(s_data);
+             }
+        }
+        g_key_file_free(keyfile);
+    }
+
+    if(default_session)
+        g_free(default_session);
+
+    exit(return_code);
+
+}
diff -Nur -x '*.orig' -x '*~' gdm-2.29.5/utils/Makefile.am gdm-2.29.5.new/utils/Makefile.am
--- gdm-2.29.5/utils/Makefile.am	2010-01-12 17:36:37.000000000 +0100
+++ gdm-2.29.5.new/utils/Makefile.am	2010-01-19 09:51:39.489451835 +0100
@@ -6,10 +6,12 @@
 	-DLOCALSTATEDIR=\""$(localstatedir)"\" 		\
 	-DGDM_SCREENSHOT_DIR=\""$(GDM_SCREENSHOT_DIR)"\"\
 	-DGNOMELOCALEDIR=\""$(datadir)/locale"\" 	\
+        -DGDMCONFDIR=\"$(gdmconfdir)\"                  \
 	$(UTILS_CFLAGS)					\
 	$(CANBERRA_GTK_CFLAGS)				\
 	$(GTK_CFLAGS)					\
 	$(COMMON_CFLAGS)				\
+	$(SET_SESSION_CFLAGS)				\
 	$(NULL)
 
 edit = sed \
@@ -36,6 +38,10 @@
 	gdm-safe-restart	\
 	$(NULL)
 
+libexec_PROGRAMS = \
+	gdm-set-default-session \
+	$(NULL)
+
 gdmflexiserver_SOURCES =	\
 	gdmflexiserver.c	\
 	$(NULL)
@@ -55,6 +61,15 @@
 	$(COMMON_LIBS)		\
 	$(NULL)
 
+gdm_set_default_session_SOURCES =	\
+	gdm-set-default-session.c	\
+	$(NULL)
+
+gdm_set_default_session_LDADD =	\
+	$(SET_SESSION_LIBS)	\
+	$(COMMON_LIBS)          \
+	$(NULL)
+
 EXTRA_DIST = 			\
 	gdm-stop.in		\
 	gdm-restart.in		\
