Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/Makefile.kmk	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/Makefile.kmk	(revision 45170)
@@ -0,0 +1,82 @@
+# $Id$
+## @file
+# Makefile for VBox LightDM greeter for providing automated logons.
+#
+
+#
+# Copyright (C) 2012 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+ifndef VBOX_OSE
+ include $(PATH_SUB_CURRENT)/liblightdm-gobject-1.5.0/Makefile.kmk
+endif
+
+# Enable building with FLTK UI + PNG support.
+VBOX_WITH_FLTK := 1
+VBOX_GREETER_WITH_PNG_SUPPORT := 1
+
+# The greeter module.
+PROGRAMS += vbox-greeter
+
+vbox-greeter_TEMPLATE = VBOXGUESTR3EXE
+vbox-greeter_DEFS     = LOG_TO_BACKDOOR VBOX_WITH_HGCM  
+vbox-greeter_DEFS    += \
+	VBOX_BUILD_TARGET=\"$(KBUILD_TARGET).$(KBUILD_TARGET_ARCH)\"
+vbox-greeter_DEFS    += \
+	$(if $(VBOX_WITH_GUEST_PROPS),VBOX_WITH_GUEST_PROPS,) \
+	$(if $(VBOX_WITH_FLTK),VBOX_WITH_FLTK,) \
+	$(if $(VBOX_GREETER_WITH_PNG_SUPPORT),VBOX_GREETER_WITH_PNG_SUPPORT,)
+
+ifndef VBOX_WITH_FLTK
+ vbox-greeter_DEFS   += \
+	GTK_DISABLE_SINGLE_INCLUDES \
+	GDK_DISABLE_DEPRECATED
+endif
+vbox-greeter_SOURCES  = vbox-greeter.cpp
+### todo: define some _INCS in Config.kmk and use 'pkg-config glib-2.0 --cflags' in configure to override 
+vbox-greeter_INCS     = \
+	/usr/lib/i386-linux-gnu/glib-2.0/include \
+	/usr/lib/x86_64-linux-gnu/glib-2.0/include \
+	/usr/include/glib-2.0 \
+	/usr/include/lightdm-gobject-1
+ifndef VBOX_WITH_FLTK
+ vbox-greeter_INCS   += \
+	/usr/include/glib-2.0 \
+	/usr/include/gtk-3.0 \
+	/usr/include/pango-1.0 \
+	/usr/include/cairo \
+	/usr/include/gdk-pixbuf-2.0 \
+	/usr/include/atk-1.0
+endif
+### todo: define some _LIBS in Config.kmk and use 'pkg-config glib-2.0 --libs' in configure to override 
+vbox-greeter_LIBS = \
+	$(if $(VBOX_OSE),lightdm-gobject-1,$(VBOX_PATH_ADDITIONS_LIB)/VBox-liblightdm-gobject$(VBOX_SUFF_LIB)) \
+	glib-2.0 \
+	gio-2.0 \
+	$(VBOX_LIB_IPRT_GUEST_R3_SHARED) \
+	$(VBOX_LIB_VBGL_R3_SHARED) \
+	$(VBOX_LIB_IPRT_GUEST_R3_SHARED)
+ifndef VBOX_WITH_FLTK
+ vbox-greeter_LIBS += gtk-3
+endif
+ifdef VBOX_WITH_FLTK
+ #vbox-greeter_LDFLAGS = -Wl,-Bsymbolic-functions -Wl,-z,relro /usr/lib/i386-linux-gnu/libfltk.a -lXext -lXft -lfontconfig -lfontconfig -lXinerama -ldl -lm -lX11
+ vbox-greeter_LDFLAGS += -s
+ vbox-greeter_LIBS += fltk
+ ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+  vbox-greeter_LIBS += fltk_images
+ endif
+endif
+
+include $(FILE_KBUILD_SUB_FOOTER)
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/Makefile.kmk	(revision 45170)
@@ -0,0 +1,43 @@
+# $Id$
+## @file
+# Makefile for liblighdm-gobject 1.5.0
+#
+
+#
+# Copyright (C) 2013 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+# The greeter module.
+LIBRARIES += VBox-liblightdm-gobject
+
+VBox-liblightdm-gobject_TEMPLATE = VBOXGUESTR3NPLIB
+VBox-liblightdm-gobject_INCS = \
+	/usr/include/glib-2.0 \
+	/usr/lib/i386-linux-gnu/glib-2.0/include \
+	/usr/lib/x86_64-linux-gnu/glib-2.0/include \
+	/usr/include/gio-unix-2.0
+VBox-liblightdm-gobject_DEFS = \
+	CONFIG_DIR=\"/etc/lightdm\" \
+	XSESSIONS_DIR=\"/usr/share/xsessions\" \
+	REMOTE_SESSIONS_DIR=\"/usr/share/lightdm/remote-sessions\"
+VBox-liblightdm-gobject_SOURCES = \
+	greeter.c \
+	language.c \
+	layout.c \
+	power.c \
+	session.c \
+	system.c \
+	user.c
+
+include $(FILE_KBUILD_SUB_FOOTER)
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/config.h	(revision 45170)
@@ -0,0 +1,90 @@
+/* config.h.  Generated from config.h.in by configure.  */
+/* config.h.in.  Generated from configure.ac by autoheader.  */
+
+/* Gettext package */
+#define GETTEXT_PACKAGE "lightdm"
+
+/* Greeter session */
+#define GREETER_SESSION "default"
+
+/* User to run greeter as */
+#define GREETER_USER "lightdm"
+
+/* Define to 1 if you have the `clearenv' function. */
+#define HAVE_CLEARENV 1
+
+/* Define to 1 if you have the <dlfcn.h> header file. */
+#define HAVE_DLFCN_H 1
+
+/* Define to 1 if you have the <inttypes.h> header file. */
+#define HAVE_INTTYPES_H 1
+
+/* Define to 1 if you have the <memory.h> header file. */
+#define HAVE_MEMORY_H 1
+
+/* Define to 1 if you have the <security/pam_appl.h> header file. */
+#define HAVE_SECURITY_PAM_APPL_H 1
+
+/* Define to 1 if you have the `setresgid' function. */
+#define HAVE_SETRESGID 1
+
+/* Define to 1 if you have the `setresuid' function. */
+#define HAVE_SETRESUID 1
+
+/* Define to 1 if you have the <stdint.h> header file. */
+#define HAVE_STDINT_H 1
+
+/* Define to 1 if you have the <stdlib.h> header file. */
+#define HAVE_STDLIB_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#define HAVE_STRINGS_H 1
+
+/* Define to 1 if you have the <string.h> header file. */
+#define HAVE_STRING_H 1
+
+/* Define to 1 if you have the <sys/stat.h> header file. */
+#define HAVE_SYS_STAT_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#define HAVE_SYS_TYPES_H 1
+
+/* Define to 1 if you have the <unistd.h> header file. */
+#define HAVE_UNISTD_H 1
+
+/* Define to the sub-directory in which libtool stores uninstalled libraries.
+   */
+#define LT_OBJDIR ".libs/"
+
+/* Define to 1 if your C compiler doesn't accept -c and -o together. */
+/* #undef NO_MINUS_C_MINUS_O */
+
+/* Name of package */
+#define PACKAGE "lightdm"
+
+/* Define to the address where bug reports for this package should be sent. */
+#define PACKAGE_BUGREPORT ""
+
+/* Define to the full name of this package. */
+#define PACKAGE_NAME "lightdm"
+
+/* Define to the full name and version of this package. */
+#define PACKAGE_STRING "lightdm 1.5.0"
+
+/* Define to the one symbol short name of this package. */
+#define PACKAGE_TARNAME "lightdm"
+
+/* Define to the home page for this package. */
+#define PACKAGE_URL ""
+
+/* Define to the version of this package. */
+#define PACKAGE_VERSION "1.5.0"
+
+/* Define to 1 if you have the ANSI C header files. */
+#define STDC_HEADERS 1
+
+/* User session */
+#define USER_SESSION "default"
+
+/* Version number of package */
+#define VERSION "1.5.0"
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/greeter.c	(revision 45170)
@@ -0,0 +1,1433 @@
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <security/pam_appl.h>
+
+#include "lightdm/greeter.h"
+
+enum {
+    PROP_0,
+    PROP_DEFAULT_SESSION_HINT,
+    PROP_HIDE_USERS_HINT,
+    PROP_SHOW_MANUAL_LOGIN_HINT,
+    PROP_SHOW_REMOTE_LOGIN_HINT,
+    PROP_LOCK_HINT,
+    PROP_HAS_GUEST_ACCOUNT_HINT,
+    PROP_SELECT_USER_HINT,
+    PROP_SELECT_GUEST_HINT,
+    PROP_AUTOLOGIN_USER_HINT,
+    PROP_AUTOLOGIN_GUEST_HINT,
+    PROP_AUTOLOGIN_TIMEOUT_HINT,
+    PROP_AUTHENTICATION_USER,
+    PROP_IN_AUTHENTICATION,
+    PROP_IS_AUTHENTICATED,
+};
+
+enum {
+    SHOW_PROMPT,
+    SHOW_MESSAGE,
+    AUTHENTICATION_COMPLETE,
+    AUTOLOGIN_TIMER_EXPIRED,
+    LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+typedef struct
+{
+    gboolean connected;
+
+    GIOChannel *to_server_channel, *from_server_channel;
+    guint8 *read_buffer;
+    gsize n_read;
+
+    gsize n_responses_waiting;
+    GList *responses_received;
+
+    GHashTable *hints;
+    guint autologin_timeout;
+
+    gchar *authentication_user;
+    gboolean in_authentication;
+    gboolean is_authenticated;
+    guint32 authenticate_sequence_number;
+    gboolean cancelling_authentication;
+} LightDMGreeterPrivate;
+
+G_DEFINE_TYPE (LightDMGreeter, lightdm_greeter, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_GREETER, LightDMGreeterPrivate)
+
+#define HEADER_SIZE 8
+#define MAX_MESSAGE_LENGTH 1024
+
+/* Messages from the greeter to the server */
+typedef enum
+{
+    GREETER_MESSAGE_CONNECT = 0,
+    GREETER_MESSAGE_AUTHENTICATE,
+    GREETER_MESSAGE_AUTHENTICATE_AS_GUEST,
+    GREETER_MESSAGE_CONTINUE_AUTHENTICATION,
+    GREETER_MESSAGE_START_SESSION,
+    GREETER_MESSAGE_CANCEL_AUTHENTICATION,
+    GREETER_MESSAGE_SET_LANGUAGE,
+    GREETER_MESSAGE_AUTHENTICATE_REMOTE
+} GreeterMessage;
+
+/* Messages from the server to the greeter */
+typedef enum
+{
+    SERVER_MESSAGE_CONNECTED = 0,
+    SERVER_MESSAGE_PROMPT_AUTHENTICATION,
+    SERVER_MESSAGE_END_AUTHENTICATION,
+    SERVER_MESSAGE_SESSION_RESULT
+} ServerMessage;
+
+/**
+ * lightdm_greeter_new:
+ *
+ * Create a new greeter.
+ *
+ * Return value: the new #LightDMGreeter
+ **/
+LightDMGreeter *
+lightdm_greeter_new ()
+{
+    return g_object_new (LIGHTDM_TYPE_GREETER, NULL);
+}
+
+static gboolean
+timed_login_cb (gpointer data)
+{
+    LightDMGreeter *greeter = data;
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+
+    priv->autologin_timeout = 0;
+    g_signal_emit (G_OBJECT (greeter), signals[AUTOLOGIN_TIMER_EXPIRED], 0);
+
+    return FALSE;
+}
+
+static guint32
+int_length ()
+{
+    return 4;
+}
+
+static void
+write_int (guint8 *buffer, gint buffer_length, guint32 value, gsize *offset)
+{
+    if (*offset + 4 >= buffer_length)
+        return;
+    buffer[*offset] = value >> 24;
+    buffer[*offset+1] = (value >> 16) & 0xFF;
+    buffer[*offset+2] = (value >> 8) & 0xFF;
+    buffer[*offset+3] = value & 0xFF;
+    *offset += 4;
+}
+
+static void
+write_string (guint8 *buffer, gint buffer_length, const gchar *value, gsize *offset)
+{
+    gint length = 0;
+
+    if (value)
+        length = strlen (value);
+    write_int (buffer, buffer_length, length, offset);
+    if (*offset + length >= buffer_length)
+        return;
+    memcpy (buffer + *offset, value, length);
+    *offset += length;
+}
+
+static guint32
+read_int (guint8 *message, gsize message_length, gsize *offset)
+{
+    guint32 value;
+    guint8 *buffer;
+
+    if (message_length - *offset < int_length ())
+    {
+        g_warning ("Not enough space for int, need %i, got %zi", int_length (), message_length - *offset);
+        return 0;
+    }
+
+    buffer = message + *offset;
+    value = buffer[0] << 24 | buffer[1] << 16 | buffer[2] << 8 | buffer[3];
+    *offset += int_length ();
+
+    return value;
+}
+
+static gchar *
+read_string (guint8 *message, gsize message_length, gsize *offset)
+{
+    guint32 length;
+    gchar *value;
+
+    length = read_int (message, message_length, offset);
+    if (message_length - *offset < length)
+    {
+        g_warning ("Not enough space for string, need %u, got %zu", length, message_length - *offset);
+        return g_strdup ("");
+    }
+
+    value = g_malloc (sizeof (gchar) * (length + 1));
+    memcpy (value, message + *offset, length);
+    value[length] = '\0';
+    *offset += length;
+
+    return value;
+}
+
+static guint32
+string_length (const gchar *value)
+{
+    if (value)
+        return int_length () + strlen (value);
+    else
+        return int_length ();
+}
+
+static void
+write_header (guint8 *buffer, gint buffer_length, guint32 id, guint32 length, gsize *offset)
+{
+    write_int (buffer, buffer_length, id, offset);
+    write_int (buffer, buffer_length, length, offset);
+}
+
+static guint32
+get_message_length (guint8 *message, gsize message_length)
+{
+    gsize offset = 4;
+    return read_int (message, message_length, &offset);
+}
+
+static void
+write_message (LightDMGreeter *greeter, guint8 *message, gsize message_length)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    GIOStatus status;
+    GError *error = NULL;
+    guint32 stated_length;
+
+    /* Double check that we're sending well-formed messages.  If we say we're
+       sending more than we do, we end up DOS'ing lightdm as it waits for the
+       rest.  If we say we're sending less than we do, we confuse the heck out
+       of lightdm, as it starts reading headers from the middle of our
+       messages. */
+    stated_length = HEADER_SIZE + get_message_length (message, message_length);
+    if (stated_length != message_length)
+    {
+        g_warning ("Refusing to write malformed packet to daemon: declared size is %u, but actual size is %zu", stated_length, message_length);
+        return;
+    }
+
+    status = g_io_channel_write_chars (priv->to_server_channel, (gchar *) message, message_length, NULL, &error);
+    if (error)
+        g_warning ("Error writing to daemon: %s", error->message);
+    g_clear_error (&error);
+    if (status == G_IO_STATUS_NORMAL)
+        g_debug ("Wrote %zi bytes to daemon", message_length);
+    g_io_channel_flush (priv->to_server_channel, NULL);
+}
+
+static void
+handle_connected (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    gchar *version;
+    GString *hint_string;
+    int timeout;
+
+    version = read_string (message, message_length, offset);
+    hint_string = g_string_new ("");
+    while (*offset < message_length)
+    {
+        gchar *name, *value;
+      
+        name = read_string (message, message_length, offset);
+        value = read_string (message, message_length, offset);
+        g_hash_table_insert (priv->hints, name, value);
+        g_string_append_printf (hint_string, " %s=%s", name, value);
+    }
+
+    g_debug ("Connected version=%s%s", version, hint_string->str);
+    g_free (version);
+    g_string_free (hint_string, TRUE);
+
+    /* Set timeout for default login */
+    timeout = lightdm_greeter_get_autologin_timeout_hint (greeter);
+    if (timeout)
+    {
+        g_debug ("Setting autologin timer for %d seconds", timeout);
+        priv->autologin_timeout = g_timeout_add (timeout * 1000, timed_login_cb, greeter);
+    }
+}
+
+static void
+handle_prompt_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    guint32 sequence_number, n_messages, i;
+    gchar *username;
+
+    sequence_number = read_int (message, message_length, offset);
+    if (sequence_number != priv->authenticate_sequence_number)
+    {
+        g_debug ("Ignoring prompt authentication with invalid sequence number %d", sequence_number);
+        return;
+    }
+
+    if (priv->cancelling_authentication)
+    {
+        g_debug ("Ignoring prompt authentication as waiting for it to cancel");
+        return;
+    }
+
+    /* Update username */
+    username = read_string (message, message_length, offset);
+    if (strcmp (username, "") == 0)
+    {
+        g_free (username);
+        username = NULL;
+    }
+    g_free (priv->authentication_user);
+    priv->authentication_user = username;
+
+    g_list_free_full (priv->responses_received, g_free);
+    priv->responses_received = NULL;
+    priv->n_responses_waiting = 0;
+
+    n_messages = read_int (message, message_length, offset);
+    g_debug ("Prompt user with %d message(s)", n_messages);
+
+    for (i = 0; i < n_messages; i++)
+    {
+        int style;
+        gchar *text;
+
+        style = read_int (message, message_length, offset);
+        text = read_string (message, message_length, offset);
+
+        // FIXME: Should stop on prompts?
+        switch (style)
+        {
+        case PAM_PROMPT_ECHO_OFF:
+            priv->n_responses_waiting++;
+            g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_SECRET);
+            break;
+        case PAM_PROMPT_ECHO_ON:
+            priv->n_responses_waiting++;
+            g_signal_emit (G_OBJECT (greeter), signals[SHOW_PROMPT], 0, text, LIGHTDM_PROMPT_TYPE_QUESTION);
+            break;
+        case PAM_ERROR_MSG:
+            g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_ERROR);
+            break;
+        case PAM_TEXT_INFO:
+            g_signal_emit (G_OBJECT (greeter), signals[SHOW_MESSAGE], 0, text, LIGHTDM_MESSAGE_TYPE_INFO);
+            break;
+        }
+
+        g_free (text);
+    }
+}
+
+static void
+handle_end_authentication (LightDMGreeter *greeter, guint8 *message, gsize message_length, gsize *offset)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    guint32 sequence_number, return_code;
+    gchar *username;
+
+    sequence_number = read_int (message, message_length, offset);
+
+    if (sequence_number != priv->authenticate_sequence_number)
+    {
+        g_debug ("Ignoring end authentication with invalid sequence number %d", sequence_number);
+        return;
+    }
+
+    username = read_string (message, message_length, offset);
+    return_code = read_int (message, message_length, offset);
+
+    g_debug ("Authentication complete for user %s with return code %d", username, return_code);
+
+    /* Update username */
+    if (strcmp (username, "") == 0)
+    {
+        g_free (username);
+        username = NULL;
+    }
+    g_free (priv->authentication_user);
+    priv->authentication_user = username;
+
+    priv->cancelling_authentication = FALSE;
+    priv->is_authenticated = (return_code == 0);
+
+    priv->in_authentication = FALSE;
+    g_signal_emit (G_OBJECT (greeter), signals[AUTHENTICATION_COMPLETE], 0);
+}
+
+static guint8 *
+read_message (LightDMGreeter *greeter, gsize *length, gboolean block)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+    gsize n_to_read, n_read;
+    guint8 *buffer;
+    GError *error = NULL;
+
+    /* Read the header, or the whole message if we already have that */
+    n_to_read = HEADER_SIZE;
+    if (priv->n_read >= HEADER_SIZE)
+        n_to_read += get_message_length (priv->read_buffer, priv->n_read);
+
+    do
+    {
+        GIOStatus status;
+        status = g_io_channel_read_chars (priv->from_server_channel,
+                                          (gchar *) priv->read_buffer + priv->n_read,
+                                          n_to_read - priv->n_read,
+                                          &n_read,
+                                          &error);
+        if (error)
+            g_warning ("Error reading from server: %s", error->message);
+        g_clear_error (&error);
+        if (status != G_IO_STATUS_NORMAL)
+            break;
+
+        g_debug ("Read %zi bytes from daemon", n_read);
+
+        priv->n_read += n_read;
+    } while (priv->n_read < n_to_read && block);
+
+    /* Stop if haven't got all the data we want */
+    if (priv->n_read != n_to_read)
+        return FALSE;
+
+    /* If have header, rerun for content */
+    if (priv->n_read == HEADER_SIZE)
+    {
+        n_to_read = get_message_length (priv->read_buffer, priv->n_read);
+        if (n_to_read > 0)
+        {
+            priv->read_buffer = g_realloc (priv->read_buffer, HEADER_SIZE + n_to_read);
+            return read_message (greeter, length, block);
+        }
+    }
+
+    buffer = priv->read_buffer;
+    *length = priv->n_read;
+
+    priv->read_buffer = g_malloc (priv->n_read);
+    priv->n_read = 0;
+
+    return buffer;
+}
+
+static gboolean
+from_server_cb (GIOChannel *source, GIOCondition condition, gpointer data)
+{
+    LightDMGreeter *greeter = data;
+    guint8 *message;
+    gsize message_length, offset;
+    guint32 id;
+
+    message = read_message (greeter, &message_length, FALSE);
+    if (!message)
+        return TRUE;
+
+    offset = 0;
+    id = read_int (message, message_length, &offset);
+    read_int (message, message_length, &offset);
+    switch (id)
+    {
+    case SERVER_MESSAGE_PROMPT_AUTHENTICATION:
+        handle_prompt_authentication (greeter, message, message_length, &offset);
+        break;
+    case SERVER_MESSAGE_END_AUTHENTICATION:
+        handle_end_authentication (greeter, message, message_length, &offset);
+        break;
+    default:
+        g_warning ("Unknown message from server: %d", id);
+        break;
+    }
+    g_free (message);
+
+    return TRUE;
+}
+
+/**
+ * lightdm_greeter_connect_sync:
+ * @greeter: The greeter to connect
+ * @error: return location for a #GError, or %NULL
+ *
+ * Connects the greeter to the display manager.  Will block until connected.
+ *
+ * Return value: #TRUE if successfully connected
+ **/
+gboolean
+lightdm_greeter_connect_sync (LightDMGreeter *greeter, GError **error)
+{
+    LightDMGreeterPrivate *priv;
+    const gchar *fd;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    guint8 *response;
+    gsize response_length, offset = 0;
+    guint32 id;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+
+    priv = GET_PRIVATE (greeter);
+
+    fd = g_getenv ("LIGHTDM_TO_SERVER_FD");
+    if (!fd)
+    {
+        g_warning ("No LIGHTDM_TO_SERVER_FD environment variable");
+        return FALSE;
+    }
+    priv->to_server_channel = g_io_channel_unix_new (atoi (fd));
+    g_io_channel_set_encoding (priv->to_server_channel, NULL, NULL);
+
+    fd = g_getenv ("LIGHTDM_FROM_SERVER_FD");
+    if (!fd)
+    {
+        g_warning ("No LIGHTDM_FROM_SERVER_FD environment variable");
+        return FALSE;
+    }
+    priv->from_server_channel = g_io_channel_unix_new (atoi (fd));
+    g_io_channel_set_encoding (priv->from_server_channel, NULL, NULL);
+    g_io_add_watch (priv->from_server_channel, G_IO_IN, from_server_cb, greeter);
+
+    g_debug ("Connecting to display manager...");
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONNECT, string_length (VERSION), &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, VERSION, &offset);
+    write_message (greeter, message, offset);
+
+    response = read_message (greeter, &response_length, TRUE);
+    if (!response)
+        return FALSE;
+
+    offset = 0;
+    id = read_int (response, response_length, &offset);
+    read_int (response, response_length, &offset);
+    if (id == SERVER_MESSAGE_CONNECTED)
+        handle_connected (greeter, response, response_length, &offset);
+    g_free (response);
+    if (id != SERVER_MESSAGE_CONNECTED)
+    {
+        g_warning ("Expected CONNECTED message, got %d", id);
+        return FALSE;
+    }
+
+    priv->connected = TRUE;
+
+    return TRUE;
+}
+
+/**
+ * lightdm_greeter_get_hint:
+ * @greeter: A #LightDMGreeter
+ * @name: The hint name to query.
+ *
+ * Get a hint.
+ *
+ * Return value: The value for this hint or #NULL if not set.
+ **/
+const gchar *
+lightdm_greeter_get_hint (LightDMGreeter *greeter, const gchar *name)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+    return g_hash_table_lookup (GET_PRIVATE (greeter)->hints, name);
+}
+
+/**
+ * lightdm_greeter_get_default_session_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the default session to use.
+ *
+ * Return value: The session name
+ **/
+const gchar *
+lightdm_greeter_get_default_session_hint (LightDMGreeter *greeter)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+    return lightdm_greeter_get_hint (greeter, "default-session");
+}
+
+/**
+ * lightdm_greeter_get_hide_users_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if user accounts should be shown.  If this is TRUE then the list of
+ * accounts should be taken from #LightDMUserList and displayed in the greeter
+ * for the user to choose from.  Note that this list can be empty and it is
+ * recommended you show a method for the user to enter a username manually.
+ * 
+ * If this option is shown the greeter should only allow these users to be
+ * chosen for login unless the manual login hint is set.
+ *
+ * Return value: #TRUE if the available users should not be shown.
+ */
+gboolean
+lightdm_greeter_get_hide_users_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "hide-users");
+
+    return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_show_manual_login_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if a manual login option should be shown.  If set the GUI
+ * should provide a way for a username to be entered manually.
+ * Without this hint a greeter which is showing a user list can
+ * limit logins to only those users.
+ *
+ * Return value: #TRUE if a manual login option should be shown.
+ */
+gboolean
+lightdm_greeter_get_show_manual_login_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "show-manual-login");
+
+    return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_show_remote_login_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if a remote login option should be shown.  If set the GUI
+ * should provide a way for a user to log into a remote desktop server.
+ *
+ * Return value: #TRUE if a remote login option should be shown.
+ */
+gboolean
+lightdm_greeter_get_show_remote_login_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "show-remote-login");
+
+    return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_lock_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if the greeter is acting as a lock screen.
+ *
+ * Return value: #TRUE if the greeter was triggered by locking the seat.
+ */
+gboolean
+lightdm_greeter_get_lock_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "lock-screen");
+
+    return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_has_guest_account_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if guest sessions are supported.
+ *
+ * Return value: #TRUE if guest sessions are supported.
+ */
+gboolean
+lightdm_greeter_get_has_guest_account_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "has-guest-account");
+  
+    return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_select_user_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the user to select by default.
+ *
+ * Return value: A username
+ */
+const gchar *
+lightdm_greeter_get_select_user_hint (LightDMGreeter *greeter)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+    return lightdm_greeter_get_hint (greeter, "select-user");
+}
+
+/**
+ * lightdm_greeter_get_select_guest_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if the guest account should be selected by default.
+ *
+ * Return value: #TRUE if the guest account should be selected by default.
+ */
+gboolean
+lightdm_greeter_get_select_guest_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "select-guest");
+  
+    return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_autologin_user_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the user account to automatically logg into when the timer expires.
+ *
+ * Return value: The user account to automatically log into.
+ */
+const gchar *
+lightdm_greeter_get_autologin_user_hint (LightDMGreeter *greeter)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+    return lightdm_greeter_get_hint (greeter, "autologin-user");
+}
+
+/**
+ * lightdm_greeter_get_autologin_guest_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Check if the guest account should be automatically logged into when the timer expires.
+ *
+ * Return value: #TRUE if the guest account should be automatically logged into.
+ */
+gboolean
+lightdm_greeter_get_autologin_guest_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "autologin-guest");
+  
+    return g_strcmp0 (value, "true") == 0;
+}
+
+/**
+ * lightdm_greeter_get_autologin_timeout_hint:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the number of seconds to wait before automaitcally logging in.
+ *
+ * Return value: The number of seconds to wait before automatically logging in or 0 for no timeout.
+ */
+gint
+lightdm_greeter_get_autologin_timeout_hint (LightDMGreeter *greeter)
+{
+    const gchar *value;
+    gint timeout = 0;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    value = lightdm_greeter_get_hint (greeter, "autologin-timeout");
+    if (value)
+        timeout = atoi (value);
+    if (timeout < 0)
+        timeout = 0;
+
+    return timeout;
+}
+
+/**
+ * lightdm_greeter_cancel_autologin:
+ * @greeter: A #LightDMGreeter
+ *
+ * Cancel the automatic login.
+ */
+void
+lightdm_greeter_cancel_autologin (LightDMGreeter *greeter)
+{
+    LightDMGreeterPrivate *priv;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    if (priv->autologin_timeout)
+       g_source_remove (priv->autologin_timeout);
+    priv->autologin_timeout = 0;
+}
+
+/**
+ * lightdm_greeter_authenticate:
+ * @greeter: A #LightDMGreeter
+ * @username: (allow-none): A username or #NULL to prompt for a username.
+ *
+ * Starts the authentication procedure for a user.
+ **/
+void
+lightdm_greeter_authenticate (LightDMGreeter *greeter, const gchar *username)
+{
+    LightDMGreeterPrivate *priv;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_if_fail (priv->connected);
+
+    priv->cancelling_authentication = FALSE;
+    priv->authenticate_sequence_number++;
+    priv->in_authentication = TRUE;  
+    priv->is_authenticated = FALSE;
+    if (username != priv->authentication_user)
+    {
+        g_free (priv->authentication_user);
+        priv->authentication_user = g_strdup (username);
+    }
+
+    g_debug ("Starting authentication for user %s...", username);
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE, int_length () + string_length (username), &offset);
+    write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
+    write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_authenticate_as_guest:
+ * @greeter: A #LightDMGreeter
+ *
+ * Starts the authentication procedure for the guest user.
+ **/
+void
+lightdm_greeter_authenticate_as_guest (LightDMGreeter *greeter)
+{
+    LightDMGreeterPrivate *priv;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_if_fail (priv->connected);
+
+    priv->cancelling_authentication = FALSE;
+    priv->authenticate_sequence_number++;
+    priv->in_authentication = TRUE;
+    priv->is_authenticated = FALSE;
+    g_free (priv->authentication_user);
+    priv->authentication_user = NULL;
+
+    g_debug ("Starting authentication for guest account...");
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_AS_GUEST, int_length (), &offset);
+    write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
+    write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_authenticate_autologin:
+ * @greeter: A #LightDMGreeter
+ *
+ * Starts the authentication procedure for the automatic login user.
+ **/
+void
+lightdm_greeter_authenticate_autologin (LightDMGreeter *greeter)
+{
+    const gchar *user;
+
+    user = lightdm_greeter_get_autologin_user_hint (greeter);
+    if (lightdm_greeter_get_autologin_guest_hint (greeter))
+        lightdm_greeter_authenticate_as_guest (greeter);
+    else if (user)
+        lightdm_greeter_authenticate (greeter, user);
+}
+
+/**
+ * lightdm_greeter_authenticate_remote:
+ * @greeter: A #LightDMGreeter
+ * @session: The name of a remote session
+ * @username: (allow-none): A username of #NULL to prompt for a username.
+ *
+ * Start authentication for a remote session type.
+ **/
+void
+lightdm_greeter_authenticate_remote (LightDMGreeter *greeter, const gchar *session, const gchar *username)
+{
+    LightDMGreeterPrivate *priv;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_if_fail (priv->connected);
+
+    priv->cancelling_authentication = FALSE;
+    priv->authenticate_sequence_number++;
+    priv->in_authentication = TRUE;
+    priv->is_authenticated = FALSE;
+    g_free (priv->authentication_user);
+    priv->authentication_user = NULL;
+
+    if (username)
+        g_debug ("Starting authentication for remote session %s as user %s...", session, username);
+    else
+        g_debug ("Starting authentication for remote session %s...", session);
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_AUTHENTICATE_REMOTE, int_length () + string_length (session) + string_length (username), &offset);
+    write_int (message, MAX_MESSAGE_LENGTH, priv->authenticate_sequence_number, &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, username, &offset);
+    write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_respond:
+ * @greeter: A #LightDMGreeter
+ * @response: Response to a prompt
+ *
+ * Provide response to a prompt.  May be one in a series.
+ **/
+void
+lightdm_greeter_respond (LightDMGreeter *greeter, const gchar *response)
+{
+    LightDMGreeterPrivate *priv;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+    g_return_if_fail (response != NULL);
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_if_fail (priv->connected);
+    g_return_if_fail (priv->n_responses_waiting > 0);
+
+    priv->n_responses_waiting--;
+    priv->responses_received = g_list_append (priv->responses_received, g_strdup (response));
+
+    if (priv->n_responses_waiting == 0)
+    {
+      guint32 msg_length;
+      GList *iter;
+
+      g_debug ("Providing response to display manager");
+
+      msg_length = int_length ();
+      for (iter = priv->responses_received; iter; iter = iter->next)
+      {
+          msg_length += string_length ((gchar *)iter->data);
+      }
+
+      write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CONTINUE_AUTHENTICATION, msg_length, &offset);
+      write_int (message, MAX_MESSAGE_LENGTH, g_list_length (priv->responses_received), &offset);
+      for (iter = priv->responses_received; iter; iter = iter->next)
+      {
+          write_string (message, MAX_MESSAGE_LENGTH, (gchar *)iter->data, &offset);
+      }
+      write_message (greeter, message, offset);
+
+      g_list_free_full (priv->responses_received, g_free);
+      priv->responses_received = NULL;
+    }
+}
+
+/**
+ * lightdm_greeter_cancel_authentication:
+ * @greeter: A #LightDMGreeter
+ *
+ * Cancel the current user authentication.
+ **/
+void
+lightdm_greeter_cancel_authentication (LightDMGreeter *greeter)
+{
+    LightDMGreeterPrivate *priv;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_if_fail (priv->connected);
+
+    priv->cancelling_authentication = TRUE;
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_CANCEL_AUTHENTICATION, 0, &offset);
+    write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_get_in_authentication:
+ * @greeter: A #LightDMGreeter
+ *
+ * Checks if the greeter is in the process of authenticating.
+ *
+ * Return value: #TRUE if the greeter is authenticating a user.
+ **/
+gboolean
+lightdm_greeter_get_in_authentication (LightDMGreeter *greeter)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    return GET_PRIVATE (greeter)->in_authentication;
+}
+
+/**
+ * lightdm_greeter_get_is_authenticated:
+ * @greeter: A #LightDMGreeter
+ *
+ * Checks if the greeter has successfully authenticated.
+ *
+ * Return value: #TRUE if the greeter is authenticated for login.
+ **/
+gboolean
+lightdm_greeter_get_is_authenticated (LightDMGreeter *greeter)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+    return GET_PRIVATE (greeter)->is_authenticated;
+}
+
+/**
+ * lightdm_greeter_get_authentication_user:
+ * @greeter: A #LightDMGreeter
+ *
+ * Get the user that is being authenticated.
+ *
+ * Return value: The username of the authentication user being authenticated or #NULL if no authentication in progress.
+ */
+const gchar *
+lightdm_greeter_get_authentication_user (LightDMGreeter *greeter)
+{
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), NULL);
+    return GET_PRIVATE (greeter)->authentication_user;
+}
+
+/**
+ * lightdm_greeter_set_language:
+ * @greeter: A #LightDMGreeter
+ * @language: The language to use for this user.
+ *
+ * Set the language for the currently authenticated user.
+ **/
+void
+lightdm_greeter_set_language (LightDMGreeter *greeter, const gchar *language)
+{
+    LightDMGreeterPrivate *priv;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    gsize offset = 0;
+
+    g_return_if_fail (LIGHTDM_IS_GREETER (greeter));
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_if_fail (priv->connected);
+
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_SET_LANGUAGE, string_length (language), &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, language, &offset);
+    write_message (greeter, message, offset);
+}
+
+/**
+ * lightdm_greeter_start_session_sync:
+ * @greeter: A #LightDMGreeter
+ * @session: (allow-none): The session to log into or #NULL to use the default.
+ * @error: return location for a #GError, or %NULL
+ *
+ * Start a session for the authenticated user.
+ *
+ * Return value: TRUE if the session was started.
+ **/
+gboolean
+lightdm_greeter_start_session_sync (LightDMGreeter *greeter, const gchar *session, GError **error)
+{
+    LightDMGreeterPrivate *priv;
+    guint8 message[MAX_MESSAGE_LENGTH];
+    guint8 *response;
+    gsize response_length, offset = 0;
+    guint32 id, return_code = 1;
+
+    g_return_val_if_fail (LIGHTDM_IS_GREETER (greeter), FALSE);
+
+    priv = GET_PRIVATE (greeter);
+
+    g_return_val_if_fail (priv->connected, FALSE);
+    g_return_val_if_fail (priv->is_authenticated, FALSE);
+
+    if (session)
+        g_debug ("Starting session %s", session);
+    else
+        g_debug ("Starting default session");
+
+    write_header (message, MAX_MESSAGE_LENGTH, GREETER_MESSAGE_START_SESSION, string_length (session), &offset);
+    write_string (message, MAX_MESSAGE_LENGTH, session, &offset);
+    write_message (greeter, message, offset);
+
+    response = read_message (greeter, &response_length, TRUE);
+    if (!response)
+        return FALSE;
+
+    offset = 0;
+    id = read_int (response, response_length, &offset);
+    read_int (response, response_length, &offset);
+    if (id == SERVER_MESSAGE_SESSION_RESULT)
+        return_code = read_int (response, response_length, &offset);
+    else
+        g_warning ("Expected SESSION_RESULT message, got %d", id);
+
+    g_free (response);
+
+    return return_code == 0;
+}
+
+static void
+lightdm_greeter_init (LightDMGreeter *greeter)
+{
+    LightDMGreeterPrivate *priv = GET_PRIVATE (greeter);
+
+    priv->read_buffer = g_malloc (HEADER_SIZE);
+    priv->hints = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
+}
+
+static void
+lightdm_greeter_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_greeter_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+    LightDMGreeter *self;
+
+    self = LIGHTDM_GREETER (object);
+
+    switch (prop_id) {
+    case PROP_DEFAULT_SESSION_HINT:
+        g_value_set_string (value, lightdm_greeter_get_default_session_hint (self));
+        break;
+    case PROP_HIDE_USERS_HINT:
+        g_value_set_boolean (value, lightdm_greeter_get_hide_users_hint (self));
+        break;
+    case PROP_SHOW_MANUAL_LOGIN_HINT:
+        g_value_set_boolean (value, lightdm_greeter_get_show_manual_login_hint (self));
+        break;
+    case PROP_SHOW_REMOTE_LOGIN_HINT:
+        g_value_set_boolean (value, lightdm_greeter_get_show_remote_login_hint (self));
+        break;
+    case PROP_LOCK_HINT:
+        g_value_set_boolean (value, lightdm_greeter_get_lock_hint (self));
+        break;
+    case PROP_HAS_GUEST_ACCOUNT_HINT:
+        g_value_set_boolean (value, lightdm_greeter_get_has_guest_account_hint (self));
+        break;
+    case PROP_SELECT_USER_HINT:
+        g_value_set_string (value, lightdm_greeter_get_select_user_hint (self));
+        break;
+    case PROP_SELECT_GUEST_HINT:
+        g_value_set_boolean (value, lightdm_greeter_get_select_guest_hint (self));
+        break;
+    case PROP_AUTOLOGIN_USER_HINT:
+        g_value_set_string (value, lightdm_greeter_get_autologin_user_hint (self));
+        break;
+    case PROP_AUTOLOGIN_GUEST_HINT:
+        g_value_set_boolean (value, lightdm_greeter_get_autologin_guest_hint (self));
+        break;
+    case PROP_AUTOLOGIN_TIMEOUT_HINT:
+        g_value_set_int (value, lightdm_greeter_get_autologin_timeout_hint (self));
+        break;
+    case PROP_AUTHENTICATION_USER:
+        g_value_set_string (value, lightdm_greeter_get_authentication_user (self));
+        break;
+    case PROP_IN_AUTHENTICATION:
+        g_value_set_boolean (value, lightdm_greeter_get_in_authentication (self));
+        break;
+    case PROP_IS_AUTHENTICATED:
+        g_value_set_boolean (value, lightdm_greeter_get_is_authenticated (self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+marshal_VOID__STRING_INT (GClosure     *closure,
+                          GValue       *return_value G_GNUC_UNUSED,
+                          guint         n_param_values,
+                          const GValue *param_values,
+                          gpointer      invocation_hint G_GNUC_UNUSED,
+                          gpointer      marshal_data)
+{
+    typedef void (*GMarshalFunc_VOID__STRING_INT) (gpointer     data1,
+                                                   gpointer     arg_1,
+                                                   gint         arg_2,
+                                                   gpointer     data2);
+    register GMarshalFunc_VOID__STRING_INT callback;
+    register GCClosure *cc = (GCClosure*) closure;
+    register gpointer data1, data2;
+
+    g_return_if_fail (n_param_values == 3);
+
+    if (G_CCLOSURE_SWAP_DATA (closure))
+    {
+        data1 = closure->data;
+        data2 = g_value_peek_pointer (param_values + 0);
+    }
+    else
+    {
+        data1 = g_value_peek_pointer (param_values + 0);
+        data2 = closure->data;
+    }
+    callback = (GMarshalFunc_VOID__STRING_INT) (marshal_data ? marshal_data : cc->callback);
+
+    callback (data1,
+              (param_values + 1)->data[0].v_pointer,
+              (param_values + 2)->data[0].v_int,
+              data2);
+}
+
+static void
+lightdm_greeter_finalize (GObject *object)
+{
+    LightDMGreeter *self = LIGHTDM_GREETER (object);
+    LightDMGreeterPrivate *priv = GET_PRIVATE (self);
+
+    if (priv->to_server_channel)
+        g_io_channel_unref (priv->to_server_channel);
+    if (priv->from_server_channel)
+        g_io_channel_unref (priv->from_server_channel);
+    g_free (priv->authentication_user);
+    g_hash_table_unref (priv->hints);
+
+    G_OBJECT_CLASS (lightdm_greeter_parent_class)->finalize (object);
+}
+
+static void
+lightdm_greeter_class_init (LightDMGreeterClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    g_type_class_add_private (klass, sizeof (LightDMGreeterPrivate));
+
+    object_class->set_property = lightdm_greeter_set_property;
+    object_class->get_property = lightdm_greeter_get_property;
+    object_class->finalize = lightdm_greeter_finalize;
+
+    g_object_class_install_property (object_class,
+                                     PROP_DEFAULT_SESSION_HINT,
+                                     g_param_spec_string ("default-session-hint",
+                                                          "default-session-hint",
+                                                          "Default session hint",
+                                                          NULL,
+                                                          G_PARAM_READWRITE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_HIDE_USERS_HINT,
+                                     g_param_spec_boolean ("hide-users-hint",
+                                                           "hide-users-hint",
+                                                           "Hide users hint",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_SHOW_MANUAL_LOGIN_HINT,
+                                     g_param_spec_boolean ("show-manual-login-hint",
+                                                           "show-manual-login-hint",
+                                                           "Show manual login hint",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_SHOW_REMOTE_LOGIN_HINT,
+                                     g_param_spec_boolean ("show-remote-login-hint",
+                                                           "show-remote-login-hint",
+                                                           "Show remote login hint",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_LOCK_HINT,
+                                     g_param_spec_boolean ("lock-hint",
+                                                           "lock-hint",
+                                                           "Lock hint",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_HAS_GUEST_ACCOUNT_HINT,
+                                     g_param_spec_boolean ("has-guest-account-hint",
+                                                           "has-guest-account-hint",
+                                                           "Has guest account hint",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_SELECT_USER_HINT,
+                                     g_param_spec_string ("select-user-hint",
+                                                          "select-user-hint",
+                                                          "Select user hint",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_SELECT_GUEST_HINT,
+                                     g_param_spec_boolean ("select-guest-hint",
+                                                           "select-guest-hint",
+                                                           "Select guest account hint",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_AUTOLOGIN_USER_HINT,
+                                     g_param_spec_string ("autologin-user-hint",
+                                                          "autologin-user-hint",
+                                                          "Autologin user hint",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_AUTOLOGIN_GUEST_HINT,
+                                     g_param_spec_boolean ("autologin-guest-hint",
+                                                           "autologin-guest-hint",
+                                                           "Autologin guest account hint",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_AUTOLOGIN_TIMEOUT_HINT,
+                                     g_param_spec_int ("autologin-timeout-hint",
+                                                       "autologin-timeout-hint",
+                                                       "Autologin timeout hint",
+                                                       0, G_MAXINT, 0,
+                                                       G_PARAM_READABLE));
+
+    g_object_class_install_property (object_class,
+                                     PROP_AUTHENTICATION_USER,
+                                     g_param_spec_string ("authentication-user",
+                                                          "authentication-user",
+                                                          "The user being authenticated",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     PROP_IN_AUTHENTICATION,
+                                     g_param_spec_boolean ("in-authentication",
+                                                           "in-authentication",
+                                                           "TRUE if a user is being authenticated",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     PROP_IS_AUTHENTICATED,
+                                     g_param_spec_boolean ("is-authenticated",
+                                                           "is-authenticated",
+                                                           "TRUE if the selected user is authenticated",
+                                                           FALSE,
+                                                           G_PARAM_READABLE));
+
+    /**
+     * LightDMGreeter::show-prompt:
+     * @greeter: A #LightDMGreeter
+     * @text: Prompt text
+     * @type: Prompt type
+     *
+     * The ::show-prompt signal gets emitted when the greeter should show a
+     * prompt to the user.  The given text should be displayed and an input
+     * field for the user to provide a response.
+     *
+     * Call lightdm_greeter_respond() with the resultant input or
+     * lightdm_greeter_cancel_authentication() to abort the authentication.
+     **/
+    signals[SHOW_PROMPT] =
+        g_signal_new ("show-prompt",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMGreeterClass, show_prompt),
+                      NULL, NULL,
+                      marshal_VOID__STRING_INT,
+                      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+
+    /**
+     * LightDMGreeter::show-message:
+     * @greeter: A #LightDMGreeter
+     * @text: Message text
+     * @type: Message type
+     *
+     * The ::show-message signal gets emitted when the greeter
+     * should show a message to the user.
+     **/
+    signals[SHOW_MESSAGE] =
+        g_signal_new ("show-message",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMGreeterClass, show_message),
+                      NULL, NULL,
+                      marshal_VOID__STRING_INT,
+                      G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_INT);
+
+    /**
+     * LightDMGreeter::authentication-complete:
+     * @greeter: A #LightDMGreeter
+     *
+     * The ::authentication-complete signal gets emitted when the greeter
+     * has completed authentication.
+     *
+     * Call lightdm_greeter_get_is_authenticated() to check if the authentication
+     * was successful.
+     **/
+    signals[AUTHENTICATION_COMPLETE] =
+        g_signal_new ("authentication-complete",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMGreeterClass, authentication_complete),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+
+    /**
+     * LightDMGreeter::autologin-timer-expired:
+     * @greeter: A #LightDMGreeter
+     *
+     * The ::timed-login signal gets emitted when the automatic login timer has expired.
+     * The application should then call lightdm_greeter_login().
+     **/
+    signals[AUTOLOGIN_TIMER_EXPIRED] =
+        g_signal_new ("autologin-timer-expired",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMGreeterClass, autologin_timer_expired),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+}
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/language.c	(revision 45170)
@@ -0,0 +1,407 @@
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+#include <string.h>
+#include <locale.h>
+#include <langinfo.h>
+#include <stdio.h>
+#include <glib/gi18n.h>
+
+#include "lightdm/language.h"
+
+enum {
+    PROP_0,
+    PROP_CODE,
+    PROP_NAME,
+    PROP_TERRITORY
+};
+
+typedef struct
+{
+    gchar *code;
+    gchar *name;
+    gchar *territory;
+} LightDMLanguagePrivate;
+
+G_DEFINE_TYPE (LightDMLanguage, lightdm_language, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LANGUAGE, LightDMLanguagePrivate)
+
+static gboolean have_languages = FALSE;
+static GList *languages = NULL;
+
+static void
+update_languages (void)
+{
+    gchar *command = "locale -a";
+    gchar *stdout_text = NULL, *stderr_text = NULL;
+    gint exit_status;
+    gboolean result;
+    GError *error = NULL;
+
+    if (have_languages)
+        return;
+
+    result = g_spawn_command_line_sync (command, &stdout_text, &stderr_text, &exit_status, &error);
+    if (error)
+    {
+        g_warning ("Failed to run '%s': %s", command, error->message);
+        g_clear_error (&error);
+    }
+    else if (exit_status != 0)
+        g_warning ("Failed to get languages, '%s' returned %d", command, exit_status);
+    else if (result)
+    {
+        gchar **tokens;
+        int i;
+
+        tokens = g_strsplit_set (stdout_text, "\n\r", -1);
+        for (i = 0; tokens[i]; i++)
+        {
+            LightDMLanguage *language;
+            gchar *code;
+
+            code = g_strchug (tokens[i]);
+            if (code[0] == '\0')
+                continue;
+
+            /* Ignore the non-interesting languages */
+            if (strcmp (command, "locale -a") == 0 && !g_strrstr (code, ".utf8"))
+                continue;
+
+            language = g_object_new (LIGHTDM_TYPE_LANGUAGE, "code", code, NULL);
+            languages = g_list_append (languages, language);
+        }
+
+        g_strfreev (tokens);
+    }
+
+    g_free (stdout_text);
+    g_free (stderr_text);
+
+    have_languages = TRUE;
+}
+
+static gboolean
+is_utf8 (const gchar *code)
+{
+    return g_strrstr (code, ".utf8") || g_strrstr (code, ".UTF-8");
+}
+
+/* Get a valid locale name that can be passed to setlocale(), so we always can use nl_langinfo() to get language and country names. */
+static gchar *
+get_locale_name (const gchar *code)
+{
+    gchar *locale = NULL, *language;
+    char *at;
+    static gchar **avail_locales;
+    gint i;
+
+    if (is_utf8 (code))
+        return (gchar *) code;
+
+    if ((at = strchr (code, '@')))
+        language = g_strndup (code, at - code);
+    else
+        language = g_strdup (code);
+
+    if (!avail_locales)
+    {
+        gchar *locales;
+        GError *error = NULL;
+
+        if (g_spawn_command_line_sync ("locale -a", &locales, NULL, NULL, &error))
+        {
+            avail_locales = g_strsplit (g_strchomp (locales), "\n", -1);
+            g_free (locales);
+        }
+        else
+        {
+            g_warning ("Failed to run 'locale -a': %s", error->message);
+            g_clear_error (&error);
+        }
+    }
+
+    if (avail_locales)
+    {
+        for (i = 0; avail_locales[i]; i++)
+        {
+            gchar *loc = avail_locales[i];
+            if (!g_strrstr (loc, ".utf8"))
+                continue;
+            if (g_str_has_prefix (loc, language))
+            {
+                locale = g_strdup (loc);
+                break;
+            }
+        }
+    }
+
+    g_free (language);
+
+    return locale;
+}
+
+/**
+ * lightdm_get_language:
+ *
+ * Get the current language.
+ *
+ * Return value: (transfer none): The current language or #NULL if no language.
+ **/
+LightDMLanguage *
+lightdm_get_language (void)
+{
+    const gchar *lang;
+    GList *link;
+
+    lang = g_getenv ("LANG");
+    if (!lang)
+        return NULL;
+
+    for (link = lightdm_get_languages (); link; link = link->next)
+    {
+        LightDMLanguage *language = link->data;
+        if (lightdm_language_matches (language, lang))
+            return language;
+    }
+
+    return NULL;
+}
+
+/**
+ * lightdm_get_languages:
+ *
+ * Get a list of languages to present to the user.
+ *
+ * Return value: (element-type LightDMLanguage) (transfer none): A list of #LightDMLanguage that should be presented to the user.
+ **/
+GList *
+lightdm_get_languages (void)
+{
+    update_languages ();
+    return languages;
+}
+
+/**
+ * lightdm_language_get_code:
+ * @language: A #LightDMLanguage
+ * 
+ * Get the code of a language.
+ * 
+ * Return value: The code of the language
+ **/
+const gchar *
+lightdm_language_get_code (LightDMLanguage *language)
+{
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
+    return GET_PRIVATE (language)->code;
+}
+
+/**
+ * lightdm_language_get_name:
+ * @language: A #LightDMLanguage
+ * 
+ * Get the name of a language.
+ *
+ * Return value: The name of the language
+ **/
+const gchar *
+lightdm_language_get_name (LightDMLanguage *language)
+{
+    LightDMLanguagePrivate *priv;
+
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
+
+    priv = GET_PRIVATE (language);
+
+    if (!priv->name)
+    {
+        gchar *locale = get_locale_name (priv->code);
+        if (locale)
+        {
+            gchar *current = setlocale (LC_ALL, NULL);
+            setlocale (LC_IDENTIFICATION, locale);
+            setlocale (LC_MESSAGES, "");
+
+            gchar *language_en = nl_langinfo (_NL_IDENTIFICATION_LANGUAGE);
+            if (language_en && strlen (language_en) > 0)
+                priv->name = g_strdup (dgettext ("iso_639_3", language_en));
+
+            setlocale (LC_ALL, current);
+        }
+        if (!priv->name)
+        {
+            gchar **tokens = g_strsplit_set (priv->code, "_.@", 2);
+            priv->name = g_strdup (tokens[0]);
+            g_strfreev (tokens);
+        }
+    }
+
+    return priv->name;
+}
+
+/**
+ * lightdm_language_get_territory:
+ * @language: A #LightDMLanguage
+ * 
+ * Get the territory the language is used in.
+ * 
+ * Return value: The territory the language is used in.
+ **/
+const gchar *
+lightdm_language_get_territory (LightDMLanguage *language)
+{
+    LightDMLanguagePrivate *priv;
+
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), NULL);
+
+    priv = GET_PRIVATE (language);
+
+    if (!priv->territory && strchr (priv->code, '_'))
+    {
+        gchar *locale = get_locale_name (priv->code);
+        if (locale)
+        {
+            gchar *current = setlocale (LC_ALL, NULL);
+            setlocale (LC_IDENTIFICATION, locale);
+            setlocale (LC_MESSAGES, "");
+
+            gchar *country_en = nl_langinfo (_NL_IDENTIFICATION_TERRITORY);
+            if (country_en && strlen (country_en) > 0 && g_strcmp0 (country_en, "ISO") != 0)
+                priv->territory = g_strdup (dgettext ("iso_3166", country_en));
+
+            setlocale (LC_ALL, current);
+        }
+        if (!priv->territory)
+        {
+            gchar **tokens = g_strsplit_set (priv->code, "_.@", 3);
+            priv->territory = g_strdup (tokens[1]);
+            g_strfreev (tokens);
+        }
+    }
+
+    return priv->territory;
+}
+
+/**
+ * lightdm_language_matches:
+ * @language: A #LightDMLanguage
+ * @code: A language code
+ * 
+ * Check if a language code matches this language.
+ * 
+ * Return value: #TRUE if the code matches this language.
+ **/
+gboolean
+lightdm_language_matches (LightDMLanguage *language, const gchar *code)
+{
+    LightDMLanguagePrivate *priv;
+
+    g_return_val_if_fail (LIGHTDM_IS_LANGUAGE (language), FALSE);
+    g_return_val_if_fail (code != NULL, FALSE);
+
+    priv = GET_PRIVATE (language);
+
+    /* Handle the fact the UTF-8 is specified both as '.utf8' and '.UTF-8' */
+    if (is_utf8 (priv->code) && is_utf8 (code))
+    {
+        /* Match the characters before the '.' */
+        int i;
+        for (i = 0; priv->code[i] && code[i] && priv->code[i] == code[i] && code[i] != '.' ; i++);
+        return priv->code[i] == '.' && code[i] == '.';
+    }
+
+    return g_str_equal (priv->code, code);
+}
+
+static void
+lightdm_language_init (LightDMLanguage *language)
+{
+}
+
+static void
+lightdm_language_set_property (GObject      *object,
+                           guint         prop_id,
+                           const GValue *value,
+                           GParamSpec   *pspec)
+{
+    LightDMLanguage *self = LIGHTDM_LANGUAGE (object);
+    LightDMLanguagePrivate *priv = GET_PRIVATE (self);
+
+    switch (prop_id) {
+    case PROP_CODE:
+        g_free (priv->name);
+        priv->code = g_strdup (g_value_get_string (value));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+lightdm_language_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+    LightDMLanguage *self;
+
+    self = LIGHTDM_LANGUAGE (object);
+
+    switch (prop_id) {
+    case PROP_CODE:
+        g_value_set_string (value, lightdm_language_get_code (self));
+        break;
+    case PROP_NAME:
+        g_value_set_string (value, lightdm_language_get_name (self));
+        break;
+    case PROP_TERRITORY:
+        g_value_set_string (value, lightdm_language_get_territory (self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+lightdm_language_class_init (LightDMLanguageClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+    g_type_class_add_private (klass, sizeof (LightDMLanguagePrivate));
+
+    object_class->set_property = lightdm_language_set_property;
+    object_class->get_property = lightdm_language_get_property;
+
+    g_object_class_install_property (object_class,
+                                     PROP_CODE,
+                                     g_param_spec_string ("code",
+                                                          "code",
+                                                          "Language code",
+                                                          NULL,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+    g_object_class_install_property (object_class,
+                                     PROP_NAME,
+                                     g_param_spec_string ("name",
+                                                          "name",
+                                                          "Name of the language",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     PROP_TERRITORY,
+                                     g_param_spec_string ("territory",
+                                                          "territory",
+                                                          "Territory the language is from",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+}
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/layout.c	(revision 45170)
@@ -0,0 +1,335 @@
+/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
+ *
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+#include <libxklavier/xklavier.h>
+
+#include "lightdm/layout.h"
+
+enum {
+    PROP_0,
+    PROP_NAME,
+    PROP_SHORT_DESCRIPTION,
+    PROP_DESCRIPTION
+};
+
+typedef struct
+{
+    gchar *name;
+    gchar *short_description;
+    gchar *description;
+} LightDMLayoutPrivate;
+
+G_DEFINE_TYPE (LightDMLayout, lightdm_layout, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_LAYOUT, LightDMLayoutPrivate)
+
+static gboolean have_layouts = FALSE;
+static Display *display = NULL;
+static XklEngine *xkl_engine = NULL;
+static XklConfigRec *xkl_config = NULL;
+static GList *layouts = NULL;
+static LightDMLayout *default_layout = NULL;
+
+static gchar *
+make_layout_string (const gchar *layout, const gchar *variant)
+{
+    if (!layout || layout[0] == 0)
+        return NULL;
+    else if (!variant || variant[0] == 0)
+        return g_strdup (layout);
+    else
+        return g_strdup_printf ("%s\t%s", layout, variant);
+}
+
+static void
+parse_layout_string (const gchar *name, gchar **layout, gchar **variant)
+{
+    gchar **split;
+
+    *layout = NULL;
+    *variant = NULL;
+
+    if (!name)
+        return;
+
+    split = g_strsplit (name, "\t", 2);
+    if (split[0])
+    {
+        *layout = g_strdup (split[0]);
+        if (split[1])
+            *variant = g_strdup (split[1]);
+    }
+    g_strfreev (split);
+}
+
+static void
+variant_cb (XklConfigRegistry *config,
+           const XklConfigItem *item,
+           gpointer data)
+{
+    LightDMLayout *layout;
+    gchar *full_name;
+
+    full_name = make_layout_string (data, item->name);
+
+    layout = g_object_new (LIGHTDM_TYPE_LAYOUT, "name", full_name, "short-description", item->short_description, "description", item->description, NULL);
+    layouts = g_list_append (layouts, layout);
+
+    g_free (full_name);
+}
+
+static void
+layout_cb (XklConfigRegistry *config,
+           const XklConfigItem *item,
+           gpointer data)
+{
+    LightDMLayout *layout;
+
+    layout = g_object_new (LIGHTDM_TYPE_LAYOUT, "name", item->name, "short-description", item->short_description, "description", item->description, NULL);
+    layouts = g_list_append (layouts, layout);
+
+    xkl_config_registry_foreach_layout_variant (config, item->name, variant_cb, (gpointer) item->name);
+}
+
+/**
+ * lightdm_get_layouts:
+ *
+ * Get a list of keyboard layouts to present to the user.
+ *
+ * Return value: (element-type LightDMLayout) (transfer none): A list of #LightDMLayout that should be presented to the user.
+ **/
+GList *
+lightdm_get_layouts (void)
+{
+    XklConfigRegistry *registry;
+
+    if (have_layouts)
+        return layouts;
+
+    display = XOpenDisplay (NULL);
+    xkl_engine = xkl_engine_get_instance (display);
+    xkl_config = xkl_config_rec_new ();
+    if (!xkl_config_rec_get_from_server (xkl_config, xkl_engine))
+        g_warning ("Failed to get Xkl configuration from server");
+
+    registry = xkl_config_registry_get_instance (xkl_engine);
+    xkl_config_registry_load (registry, FALSE);
+    xkl_config_registry_foreach_layout (registry, layout_cb, NULL);
+    g_object_unref (registry);
+
+    have_layouts = TRUE;
+
+    return layouts;
+}
+
+/**
+ * lightdm_set_layout:
+ * @layout: The layout to use
+ *
+ * Set the layout for this session.
+ **/
+void
+lightdm_set_layout (LightDMLayout *dmlayout)
+{
+    XklConfigRec *config;
+    gchar *layout, *variant;
+
+    g_return_if_fail (dmlayout != NULL);
+
+    g_debug ("Setting keyboard layout to '%s'", lightdm_layout_get_name (dmlayout));
+
+    parse_layout_string (lightdm_layout_get_name (dmlayout), &layout, &variant);
+
+    config = xkl_config_rec_new ();
+    config->layouts = g_malloc (sizeof (gchar *) * 2);
+    config->variants = g_malloc (sizeof (gchar *) * 2);
+    config->model = g_strdup (xkl_config->model);
+    config->layouts[0] = layout;
+    config->layouts[1] = NULL;
+    config->variants[0] = variant;
+    config->variants[1] = NULL;
+    if (!xkl_config_rec_activate (config, xkl_engine))
+        g_warning ("Failed to activate XKL config");
+    g_object_unref (config);
+}
+
+/**
+ * lightdm_get_layout:
+ *
+ * Get the current keyboard layout.
+ *
+ * Return value: (transfer none): The currently active layout for this user.
+ **/
+LightDMLayout *
+lightdm_get_layout (void)
+{
+    lightdm_get_layouts ();
+
+    if (layouts && xkl_config && !default_layout)
+    {
+        gchar *full_name;
+        GList *item;
+
+        full_name = make_layout_string (xkl_config->layouts ? xkl_config->layouts[0] : NULL,
+                                        xkl_config->variants ? xkl_config->variants[0] : NULL);
+
+        for (item = layouts; item; item = item->next)
+        {
+            LightDMLayout *iter_layout = (LightDMLayout *) item->data;
+            if (g_strcmp0 (lightdm_layout_get_name (iter_layout), full_name) == 0)
+            {
+                default_layout = iter_layout;
+                break;
+            }
+        }
+
+        g_free (full_name);
+    }
+
+    return default_layout;
+}
+
+/**
+ * lightdm_layout_get_name:
+ * @layout: A #LightDMLayout
+ * 
+ * Get the name of a layout.
+ * 
+ * Return value: The name of the layout
+ **/
+const gchar *
+lightdm_layout_get_name (LightDMLayout *layout)
+{
+    g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL);
+    return GET_PRIVATE (layout)->name;
+}
+
+/**
+ * lightdm_layout_get_short_description:
+ * @layout: A #LightDMLayout
+ * 
+ * Get the short description of a layout.
+ *
+ * Return value: A short description of the layout
+ **/
+const gchar *
+lightdm_layout_get_short_description (LightDMLayout *layout)
+{
+    g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL);
+    return GET_PRIVATE (layout)->short_description;
+}
+
+/**
+ * lightdm_layout_get_description:
+ * @layout: A #LightDMLayout
+ * 
+ * Get the long description of a layout.
+ * 
+ * Return value: A long description of the layout
+ **/
+const gchar *
+lightdm_layout_get_description (LightDMLayout *layout)
+{
+    g_return_val_if_fail (LIGHTDM_IS_LAYOUT (layout), NULL);
+    return GET_PRIVATE (layout)->description;
+}
+
+static void
+lightdm_layout_init (LightDMLayout *layout)
+{
+}
+
+static void
+lightdm_layout_set_property (GObject      *object,
+                         guint         prop_id,
+                         const GValue *value,
+                         GParamSpec   *pspec)
+{
+    LightDMLayout *self = LIGHTDM_LAYOUT (object);
+    LightDMLayoutPrivate *priv = GET_PRIVATE (self);
+
+    switch (prop_id) {
+    case PROP_NAME:
+        g_free (priv->name);
+        priv->name = g_strdup (g_value_get_string (value));
+        break;
+    case PROP_SHORT_DESCRIPTION:
+        g_free (priv->short_description);
+        priv->short_description = g_strdup (g_value_get_string (value));
+        break;
+    case PROP_DESCRIPTION:
+        g_free (priv->description);
+        priv->description = g_strdup (g_value_get_string (value));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+lightdm_layout_get_property (GObject    *object,
+                         guint       prop_id,
+                         GValue     *value,
+                         GParamSpec *pspec)
+{
+    LightDMLayout *self;
+
+    self = LIGHTDM_LAYOUT (object);
+
+    switch (prop_id) {
+    case PROP_NAME:
+        g_value_set_string (value, lightdm_layout_get_name (self));
+        break;
+    case PROP_SHORT_DESCRIPTION:
+        g_value_set_string (value, lightdm_layout_get_short_description (self));
+        break;
+    case PROP_DESCRIPTION:
+        g_value_set_string (value, lightdm_layout_get_description (self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+lightdm_layout_class_init (LightDMLayoutClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+    g_type_class_add_private (klass, sizeof (LightDMLayoutPrivate));
+
+    object_class->set_property = lightdm_layout_set_property;
+    object_class->get_property = lightdm_layout_get_property;
+
+    g_object_class_install_property (object_class,
+                                     PROP_NAME,
+                                     g_param_spec_string ("name",
+                                                          "name",
+                                                          "Name of the layout",
+                                                          NULL,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+    g_object_class_install_property (object_class,
+                                     PROP_SHORT_DESCRIPTION,
+                                     g_param_spec_string ("short-description",
+                                                          "short-description",
+                                                          "Short description of the layout",
+                                                          NULL,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+    g_object_class_install_property (object_class,
+                                     PROP_DESCRIPTION,
+                                     g_param_spec_string ("description",
+                                                          "description",
+                                                          "Long description of the layout",
+                                                          NULL,
+                                                          G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+}
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/power.c	(revision 45170)
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+#include "config.h"
+
+#include <string.h>
+#include <gio/gio.h>
+
+#include "lightdm/power.h"
+
+static GDBusProxy *upower_proxy = NULL;
+static GDBusProxy *ck_proxy = NULL;
+
+static gboolean
+upower_call_function (const gchar *function, gboolean default_result, GError **error)
+{
+    GVariant *result;
+    gboolean function_result = FALSE;
+
+    if (!upower_proxy)
+    {
+        upower_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                      G_DBUS_PROXY_FLAGS_NONE,
+                                                      NULL,
+                                                      "org.freedesktop.UPower",
+                                                      "/org/freedesktop/UPower",
+                                                      "org.freedesktop.UPower",
+                                                      NULL,
+                                                      error);
+        if (!upower_proxy)
+            return FALSE;
+    }
+
+    result = g_dbus_proxy_call_sync (upower_proxy,
+                                     function,
+                                     NULL,
+                                     G_DBUS_CALL_FLAGS_NONE,
+                                     -1,
+                                     NULL,
+                                     error);
+    if (!result)
+        return default_result;
+
+    if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)")))
+        g_variant_get (result, "(b)", &function_result);
+
+    g_variant_unref (result);
+    return function_result;
+}
+
+/**
+ * lightdm_get_can_suspend:
+ *
+ * Checks if authorized to do a system suspend.
+ *
+ * Return value: #TRUE if can suspend the system
+ **/
+gboolean
+lightdm_get_can_suspend (void)
+{
+    return upower_call_function ("SuspendAllowed", FALSE, NULL);
+}
+
+/**
+ * lightdm_suspend:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system suspend.
+ * 
+ * Return value: #TRUE if suspend initiated.
+ **/
+gboolean
+lightdm_suspend (GError **error)
+{
+    return upower_call_function ("Suspend", TRUE, error);
+}
+
+/**
+ * lightdm_get_can_hibernate:
+ *
+ * Checks if is authorized to do a system hibernate.
+ *
+ * Return value: #TRUE if can hibernate the system
+ **/
+gboolean
+lightdm_get_can_hibernate (void)
+{
+    return upower_call_function ("HibernateAllowed", FALSE, NULL);
+}
+
+/**
+ * lightdm_hibernate:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system hibernate.
+ * 
+ * Return value: #TRUE if hibernate initiated.
+ **/
+gboolean
+lightdm_hibernate (GError **error)
+{
+    return upower_call_function ("Hibernate", TRUE, error);
+}
+
+static gboolean
+ck_call_function (const gchar *function, gboolean default_result, GError **error)
+{
+    GVariant *result;
+    gboolean function_result = FALSE;
+
+    if (!ck_proxy)
+    {
+        ck_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                  G_DBUS_PROXY_FLAGS_NONE,
+                                                  NULL,
+                                                  "org.freedesktop.ConsoleKit",
+                                                  "/org/freedesktop/ConsoleKit/Manager",
+                                                  "org.freedesktop.ConsoleKit.Manager",
+                                                  NULL,
+                                                  error);
+        if (!ck_proxy)
+            return FALSE;
+    }
+
+    result = g_dbus_proxy_call_sync (ck_proxy,
+                                     function,
+                                     NULL,
+                                     G_DBUS_CALL_FLAGS_NONE,
+                                     -1,
+                                     NULL,
+                                     error);
+
+    if (!result)
+        return default_result;
+
+    if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(b)")))
+        g_variant_get (result, "(b)", &function_result);
+
+    g_variant_unref (result);
+    return function_result;
+}
+
+/**
+ * lightdm_get_can_restart:
+ *
+ * Checks if is authorized to do a system restart.
+ *
+ * Return value: #TRUE if can restart the system
+ **/
+gboolean
+lightdm_get_can_restart (void)
+{
+    return ck_call_function ("CanRestart", FALSE, NULL);
+}
+
+/**
+ * lightdm_restart:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system restart.
+ *
+ * Return value: #TRUE if restart initiated.
+ **/
+gboolean
+lightdm_restart (GError **error)
+{
+    return ck_call_function ("Restart", TRUE, error);
+}
+
+/**
+ * lightdm_get_can_shutdown:
+ *
+ * Checks if is authorized to do a system shutdown.
+ *
+ * Return value: #TRUE if can shutdown the system
+ **/
+gboolean
+lightdm_get_can_shutdown (void)
+{
+    return ck_call_function ("CanStop", FALSE, NULL);
+}
+
+/**
+ * lightdm_shutdown:
+ * @error: return location for a #GError, or %NULL
+ *
+ * Triggers a system shutdown.
+ * 
+ * Return value: #TRUE if shutdown initiated.
+ **/
+gboolean
+lightdm_shutdown (GError **error)
+{
+    return ck_call_function ("Stop", TRUE, error);
+}
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/session.c	(revision 45170)
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+#include <string.h>
+#include <gio/gdesktopappinfo.h>
+
+#include "lightdm/session.h"
+
+enum {
+    PROP_0,
+    PROP_KEY,
+    PROP_NAME,
+    PROP_COMMENT
+};
+
+typedef struct
+{
+    gchar *key;
+    gchar *name;
+    gchar *comment;
+} LightDMSessionPrivate;
+
+G_DEFINE_TYPE (LightDMSession, lightdm_session, G_TYPE_OBJECT);
+
+#define GET_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_SESSION, LightDMSessionPrivate)
+
+static gboolean have_sessions = FALSE;
+static GList *local_sessions = NULL;
+static GList *remote_sessions = NULL;
+
+static gint 
+compare_session (gconstpointer a, gconstpointer b)
+{
+    LightDMSessionPrivate *priv_a = GET_PRIVATE (a);
+    LightDMSessionPrivate *priv_b = GET_PRIVATE (b);
+    return strcmp (priv_a->name, priv_b->name);
+}
+
+static LightDMSession *
+load_session (GKeyFile *key_file, const gchar *key)
+{
+    gchar *domain, *name;
+    LightDMSession *session;
+    LightDMSessionPrivate *priv;
+    gchar *try_exec;
+  
+    if (g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NO_DISPLAY, NULL) ||
+        g_key_file_get_boolean (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_HIDDEN, NULL))
+        return NULL;
+
+#ifdef G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN
+    domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_GETTEXT_DOMAIN, NULL);
+#else
+    domain = g_key_file_get_string (key_file, G_KEY_FILE_DESKTOP_GROUP, "X-GNOME-Gettext-Domain", NULL);
+#endif
+    name = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_NAME, domain, NULL);
+    if (!name)
+    {
+        g_warning ("Ignoring session without name");
+        g_free (domain);
+        return NULL;
+    }
+
+    try_exec = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_TRY_EXEC, domain, NULL);
+    if (try_exec)
+    {
+        gchar *full_path;
+
+        full_path = g_find_program_in_path (try_exec);
+        g_free (try_exec);
+
+        if (!full_path)
+        {
+            g_free (name);
+            g_free (domain);
+            return NULL;
+        }
+        g_free (full_path);
+    }
+
+    session = g_object_new (LIGHTDM_TYPE_SESSION, NULL);
+    priv = GET_PRIVATE (session);
+
+    g_free (priv->key);
+    priv->key = g_strdup (key);
+
+    g_free (priv->name);
+    priv->name = name;
+
+    g_free (priv->comment);
+    priv->comment = g_key_file_get_locale_string (key_file, G_KEY_FILE_DESKTOP_GROUP, G_KEY_FILE_DESKTOP_KEY_COMMENT, domain, NULL);
+    if (!priv->comment)
+        priv->comment = g_strdup ("");
+
+    g_free (domain);
+
+    return session;
+}
+
+static GList *
+load_sessions (const gchar *sessions_dir)
+{
+    GDir *directory;
+    GList *sessions = NULL;
+    GError *error = NULL;
+
+    directory = g_dir_open (sessions_dir, 0, &error);
+    if (error)
+        g_warning ("Failed to open sessions directory: %s", error->message);
+    g_clear_error (&error);
+    if (!directory)
+        return NULL;
+
+    while (TRUE)
+    {
+        const gchar *filename;
+        gchar *path;
+        GKeyFile *key_file;
+        gboolean result;
+
+        filename = g_dir_read_name (directory);
+        if (filename == NULL)
+            break;
+
+        if (!g_str_has_suffix (filename, ".desktop"))
+            continue;
+
+        path = g_build_filename (sessions_dir, filename, NULL);
+
+        key_file = g_key_file_new ();
+        result = g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, &error);
+        if (error)
+            g_warning ("Failed to load session file %s: %s:", path, error->message);
+        g_clear_error (&error);
+
+        if (result)
+        {
+            gchar *key;
+            LightDMSession *session;
+
+            key = g_strndup (filename, strlen (filename) - strlen (".desktop"));
+            session = load_session (key_file, key);
+            if (session)
+            {
+                g_debug ("Loaded session %s (%s, %s)", path, GET_PRIVATE (session)->name, GET_PRIVATE (session)->comment);
+                sessions = g_list_insert_sorted (sessions, session, compare_session);
+            }
+            else
+                g_debug ("Ignoring session %s", path);
+            g_free (key);
+        }
+
+        g_free (path);
+        g_key_file_free (key_file);
+    }
+
+    g_dir_close (directory);
+  
+    return sessions;
+}
+
+static void
+update_sessions (void)
+{
+    GKeyFile *config_key_file = NULL;
+    gchar *config_path = NULL;
+    gchar *xsessions_dir;
+    gchar *remote_sessions_dir;
+    gboolean result;
+    GError *error = NULL;
+
+    if (have_sessions)
+        return;
+
+    xsessions_dir = g_strdup (XSESSIONS_DIR);
+    remote_sessions_dir = g_strdup (REMOTE_SESSIONS_DIR);
+
+    /* Use session directory from configuration */
+    /* FIXME: This should be sent in the greeter connection */
+    config_path = g_build_filename (CONFIG_DIR, "lightdm.conf", NULL);
+    config_key_file = g_key_file_new ();
+    result = g_key_file_load_from_file (config_key_file, config_path, G_KEY_FILE_NONE, &error);
+    if (error)
+        g_warning ("Failed to open configuration file: %s", error->message);
+    g_clear_error (&error);
+    if (result)
+    {
+        gchar *value;
+      
+        value = g_key_file_get_string (config_key_file, "LightDM", "xsessions-directory", NULL);
+        if (value)
+        {
+            g_free (xsessions_dir);
+            xsessions_dir = value;
+        }
+
+        value = g_key_file_get_string (config_key_file, "LightDM", "remote-sessions-directory", NULL);
+        if (value)
+        {
+            g_free (remote_sessions_dir);
+            remote_sessions_dir = value;
+        }
+    }
+    g_key_file_free (config_key_file);
+    g_free (config_path);
+
+    local_sessions = load_sessions (xsessions_dir);
+    remote_sessions = load_sessions (remote_sessions_dir);
+
+    g_free (xsessions_dir);
+    g_free (remote_sessions_dir);
+
+    have_sessions = TRUE;
+}
+
+/**
+ * lightdm_get_sessions:
+ *
+ * Get the available sessions.
+ *
+ * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession
+ **/
+GList *
+lightdm_get_sessions (void)
+{
+    update_sessions ();
+    return local_sessions;
+}
+
+/**
+ * lightdm_get_remote_sessions:
+ *
+ * Get the available remote sessions.
+ *
+ * Return value: (element-type LightDMSession) (transfer none): A list of #LightDMSession
+ **/
+GList *
+lightdm_get_remote_sessions (void)
+{
+    update_sessions ();
+    return remote_sessions;
+}
+
+/**
+ * lightdm_session_get_key:
+ * @session: A #LightDMSession
+ * 
+ * Get the key for a session
+ * 
+ * Return value: The session key
+ **/
+const gchar *
+lightdm_session_get_key (LightDMSession *session)
+{
+    g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
+    return GET_PRIVATE (session)->key;
+}
+
+/**
+ * lightdm_session_get_name:
+ * @session: A #LightDMSession
+ * 
+ * Get the name for a session
+ * 
+ * Return value: The session name
+ **/
+const gchar *
+lightdm_session_get_name (LightDMSession *session)
+{
+    g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
+    return GET_PRIVATE (session)->name;
+}
+
+/**
+ * lightdm_session_get_comment:
+ * @session: A #LightDMSession
+ * 
+ * Get the comment for a session
+ * 
+ * Return value: The session comment
+ **/
+const gchar *
+lightdm_session_get_comment (LightDMSession *session)
+{
+    g_return_val_if_fail (LIGHTDM_IS_SESSION (session), NULL);
+    return GET_PRIVATE (session)->comment;
+}
+
+static void
+lightdm_session_init (LightDMSession *session)
+{
+}
+
+static void
+lightdm_session_set_property (GObject      *object,
+                          guint         prop_id,
+                          const GValue *value,
+                          GParamSpec   *pspec)
+{
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_session_get_property (GObject    *object,
+                          guint       prop_id,
+                          GValue     *value,
+                          GParamSpec *pspec)
+{
+    LightDMSession *self;
+
+    self = LIGHTDM_SESSION (object);
+
+    switch (prop_id) {
+    case PROP_KEY:
+        g_value_set_string (value, lightdm_session_get_key (self));
+        break;
+    case PROP_NAME:
+        g_value_set_string (value, lightdm_session_get_name (self));
+        break;
+    case PROP_COMMENT:
+        g_value_set_string (value, lightdm_session_get_comment (self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+lightdm_session_finalize (GObject *object)
+{
+    LightDMSession *self = LIGHTDM_SESSION (object);
+    LightDMSessionPrivate *priv = GET_PRIVATE (self);
+
+    g_free (priv->key);
+    g_free (priv->name);
+    g_free (priv->comment);
+}
+
+static void
+lightdm_session_class_init (LightDMSessionClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+    g_type_class_add_private (klass, sizeof (LightDMSessionPrivate));
+
+    object_class->set_property = lightdm_session_set_property;
+    object_class->get_property = lightdm_session_get_property;
+    object_class->finalize = lightdm_session_finalize;
+
+    g_object_class_install_property (object_class,
+                                     PROP_KEY,
+                                     g_param_spec_string ("key",
+                                                          "key",
+                                                          "Session key",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     PROP_NAME,
+                                     g_param_spec_string ("name",
+                                                          "name",
+                                                          "Session name",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     PROP_COMMENT,
+                                     g_param_spec_string ("comment",
+                                                          "comment",
+                                                          "Session comment",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+}
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/system.c	(revision 45170)
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2010-2011 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ *
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+#include <sys/utsname.h>
+
+#include "lightdm/system.h"
+
+static gchar *hostname = NULL;
+
+/**
+ * lightdm_get_hostname:
+ *
+ * Return value: The name of the host we are running on.
+ **/
+const gchar *
+lightdm_get_hostname (void)
+{
+    if (!hostname)
+    {
+        struct utsname info;
+        uname (&info);
+        hostname = g_strdup (info.nodename);
+    }
+
+    return hostname;
+}
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/liblightdm-gobject-1.5.0/user.c	(revision 45170)
@@ -0,0 +1,1646 @@
+/* -*- Mode: C; indent-tabs-mode:nil; tab-width:4 -*-
+ *
+ * Copyright (C) 2010 Robert Ancell.
+ * Author: Robert Ancell <robert.ancell@canonical.com>
+ * 
+ * This library is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 2 or version 3 of the License.
+ * See http://www.gnu.org/copyleft/lgpl.html the full text of the license.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <sys/utsname.h>
+#include <pwd.h>
+#include <gio/gio.h>
+
+#include "lightdm/user.h"
+
+enum
+{
+    LIST_PROP_0,
+    LIST_PROP_NUM_USERS,
+    LIST_PROP_USERS,
+};
+
+enum
+{
+    USER_PROP_0,
+    USER_PROP_NAME,
+    USER_PROP_REAL_NAME,
+    USER_PROP_DISPLAY_NAME,
+    USER_PROP_HOME_DIRECTORY,
+    USER_PROP_IMAGE,
+    USER_PROP_BACKGROUND,
+    USER_PROP_LANGUAGE,
+    USER_PROP_LAYOUT,
+    USER_PROP_LAYOUTS,
+    USER_PROP_SESSION,
+    USER_PROP_LOGGED_IN,
+    USER_PROP_HAS_MESSAGES
+};
+
+enum
+{
+    USER_ADDED,
+    USER_CHANGED,
+    USER_REMOVED,
+    LAST_LIST_SIGNAL
+};
+static guint list_signals[LAST_LIST_SIGNAL] = { 0 };
+
+enum
+{
+    CHANGED,
+    LAST_USER_SIGNAL
+};
+static guint user_signals[LAST_USER_SIGNAL] = { 0 };
+
+typedef struct
+{
+    /* Connection to AccountsService */
+    GDBusProxy *accounts_service_proxy;
+    GList *user_account_objects;
+
+    /* Connection to DisplayManager */
+    GDBusProxy *display_manager_proxy;
+
+    /* File monitor for password file */
+    GFileMonitor *passwd_monitor;
+  
+    /* TRUE if have scanned users */
+    gboolean have_users;
+
+    /* List of users */
+    GList *users;
+
+    /* List of sessions */
+    GList *sessions;
+} LightDMUserListPrivate;
+
+typedef struct
+{
+    GDBusProxy *proxy;
+    LightDMUser *user;
+} UserAccountObject;
+
+typedef struct
+{
+    LightDMUserList *user_list;
+
+    gchar *name;
+    gchar *real_name;
+    gchar *home_directory;
+    gchar *image;
+    gchar *background;
+    gboolean has_messages;
+
+    GKeyFile *dmrc_file;
+    gchar *language;
+    gchar **layouts;
+    gchar *session;
+} LightDMUserPrivate;
+
+typedef struct
+{
+    GObject parent_instance;
+    gchar *path;
+    gchar *username;
+} Session;
+
+typedef struct
+{
+    GObjectClass parent_class;
+} SessionClass;
+
+G_DEFINE_TYPE (LightDMUserList, lightdm_user_list, G_TYPE_OBJECT);
+G_DEFINE_TYPE (LightDMUser, lightdm_user, G_TYPE_OBJECT);
+#define SESSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), session_get_type (), Session))
+GType session_get_type (void);
+G_DEFINE_TYPE (Session, session, G_TYPE_OBJECT);
+
+#define GET_LIST_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER_LIST, LightDMUserListPrivate)
+#define GET_USER_PRIVATE(obj) G_TYPE_INSTANCE_GET_PRIVATE ((obj), LIGHTDM_TYPE_USER, LightDMUserPrivate)
+
+#define PASSWD_FILE      "/etc/passwd"
+#define USER_CONFIG_FILE "/etc/lightdm/users.conf"
+
+static LightDMUserList *singleton = NULL;
+
+/**
+ * lightdm_user_list_get_instance:
+ *
+ * Get the user list.
+ *
+ * Return value: (transfer none): the #LightDMUserList
+ **/
+LightDMUserList *
+lightdm_user_list_get_instance (void)
+{
+    if (!singleton)
+        singleton = g_object_new (LIGHTDM_TYPE_USER_LIST, NULL);
+    return singleton;
+}
+
+static LightDMUser *
+get_user_by_name (LightDMUserList *user_list, const gchar *username)
+{
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+    GList *link;
+  
+    for (link = priv->users; link; link = link->next)
+    {
+        LightDMUser *user = link->data;
+        if (strcmp (lightdm_user_get_name (user), username) == 0)
+            return user;
+    }
+
+    return NULL;
+}
+  
+static gint
+compare_user (gconstpointer a, gconstpointer b)
+{
+    LightDMUser *user_a = (LightDMUser *) a, *user_b = (LightDMUser *) b;
+    return strcmp (lightdm_user_get_display_name (user_a), lightdm_user_get_display_name (user_b));
+}
+
+static gboolean
+update_passwd_user (LightDMUser *user, const gchar *real_name, const gchar *home_directory, const gchar *image)
+{
+    LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+
+    if (g_strcmp0 (lightdm_user_get_real_name (user), real_name) == 0 &&
+        g_strcmp0 (lightdm_user_get_home_directory (user), home_directory) == 0 &&
+        g_strcmp0 (lightdm_user_get_image (user), image) == 0)
+        return FALSE;
+
+    g_free (priv->real_name);
+    priv->real_name = g_strdup (real_name);
+    g_free (priv->home_directory);
+    priv->home_directory = g_strdup (home_directory);
+    g_free (priv->image);
+    priv->image = g_strdup (image);
+
+    return TRUE;
+}
+
+static void
+user_changed_cb (LightDMUser *user, LightDMUserList *user_list)
+{
+    g_signal_emit (user_list, list_signals[USER_CHANGED], 0, user);
+}
+
+static void
+load_passwd_file (LightDMUserList *user_list, gboolean emit_add_signal)
+{
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+    GKeyFile *config;
+    gchar *value;
+    gint minimum_uid;
+    gchar **hidden_users, **hidden_shells;
+    GList *users = NULL, *old_users, *new_users = NULL, *changed_users = NULL, *link;
+    GError *error = NULL;
+
+    g_debug ("Loading user config from %s", USER_CONFIG_FILE);
+
+    config = g_key_file_new ();
+    g_key_file_load_from_file (config, USER_CONFIG_FILE, G_KEY_FILE_NONE, &error);
+    if (error && !g_error_matches (error, G_FILE_ERROR, G_FILE_ERROR_NOENT))
+        g_warning ("Failed to load configuration from %s: %s", USER_CONFIG_FILE, error->message); // FIXME: Don't make warning on no file, just info
+    g_clear_error (&error);
+
+    if (g_key_file_has_key (config, "UserList", "minimum-uid", NULL))
+        minimum_uid = g_key_file_get_integer (config, "UserList", "minimum-uid", NULL);
+    else
+        minimum_uid = 500;
+
+    value = g_key_file_get_string (config, "UserList", "hidden-users", NULL);
+    if (!value)
+        value = g_strdup ("nobody nobody4 noaccess");
+    hidden_users = g_strsplit (value, " ", -1);
+    g_free (value);
+
+    value = g_key_file_get_string (config, "UserList", "hidden-shells", NULL);
+    if (!value)
+        value = g_strdup ("/bin/false /usr/sbin/nologin");
+    hidden_shells = g_strsplit (value, " ", -1);
+    g_free (value);
+
+    g_key_file_free (config);
+
+    setpwent ();
+
+    while (TRUE)
+    {
+        struct passwd *entry;
+        LightDMUser *user;
+        LightDMUserPrivate *user_priv;
+        char **tokens;
+        gchar *real_name, *image;
+        int i;
+
+        errno = 0;
+        entry = getpwent ();
+        if (!entry)
+            break;
+
+        /* Ignore system users */
+        if (entry->pw_uid < minimum_uid)
+            continue;
+
+        /* Ignore users disabled by shell */
+        if (entry->pw_shell)
+        {
+            for (i = 0; hidden_shells[i] && strcmp (entry->pw_shell, hidden_shells[i]) != 0; i++);
+            if (hidden_shells[i])
+                continue;
+        }
+
+        /* Ignore certain users */
+        for (i = 0; hidden_users[i] && strcmp (entry->pw_name, hidden_users[i]) != 0; i++);
+        if (hidden_users[i])
+            continue;
+
+        tokens = g_strsplit (entry->pw_gecos, ",", -1);
+        if (tokens[0] != NULL && tokens[0][0] != '\0')
+            real_name = g_strdup (tokens[0]);
+        else
+            real_name = g_strdup ("");
+        g_strfreev (tokens);
+
+        image = g_build_filename (entry->pw_dir, ".face", NULL);
+        if (!g_file_test (image, G_FILE_TEST_EXISTS))
+        {
+            g_free (image);
+            image = g_build_filename (entry->pw_dir, ".face.icon", NULL);
+            if (!g_file_test (image, G_FILE_TEST_EXISTS))
+            {
+                g_free (image);
+                image = NULL;
+            }
+        }
+
+        user = g_object_new (LIGHTDM_TYPE_USER, NULL);
+        user_priv = GET_USER_PRIVATE (user);
+        user_priv->user_list = user_list;
+        g_free (user_priv->name);
+        user_priv->name = g_strdup (entry->pw_name);
+        g_free (user_priv->real_name);
+        user_priv->real_name = real_name;
+        g_free (user_priv->home_directory);
+        user_priv->home_directory = g_strdup (entry->pw_dir);
+        g_free (user_priv->image);
+        user_priv->image = image;
+
+        /* Update existing users if have them */
+        for (link = priv->users; link; link = link->next)
+        {
+            LightDMUser *info = link->data;
+            if (strcmp (lightdm_user_get_name (info), lightdm_user_get_name (user)) == 0)
+            {
+                if (update_passwd_user (info, lightdm_user_get_real_name (user), lightdm_user_get_home_directory (user), lightdm_user_get_image (user)))
+                    changed_users = g_list_insert_sorted (changed_users, info, compare_user);
+                g_object_unref (user);
+                user = info;
+                break;
+            }
+        }
+        if (!link)
+        {
+            /* Only notify once we have loaded the user list */
+            if (priv->have_users)
+                new_users = g_list_insert_sorted (new_users, user, compare_user);
+        }
+        users = g_list_insert_sorted (users, user, compare_user);
+    }
+    g_strfreev (hidden_users);
+    g_strfreev (hidden_shells);
+
+    if (errno != 0)
+        g_warning ("Failed to read password database: %s", strerror (errno));
+
+    endpwent ();
+
+    /* Use new user list */
+    old_users = priv->users;
+    priv->users = users;
+  
+    /* Notify of changes */
+    for (link = new_users; link; link = link->next)
+    {
+        LightDMUser *info = link->data;
+        g_debug ("User %s added", lightdm_user_get_name (info));
+        g_signal_connect (info, "changed", G_CALLBACK (user_changed_cb), user_list);
+        if (emit_add_signal)
+            g_signal_emit (user_list, list_signals[USER_ADDED], 0, info);
+    }
+    g_list_free (new_users);
+    for (link = changed_users; link; link = link->next)
+    {
+        LightDMUser *info = link->data;
+        g_debug ("User %s changed", lightdm_user_get_name (info));
+        g_signal_emit (info, user_signals[CHANGED], 0);
+    }
+    g_list_free (changed_users);
+    for (link = old_users; link; link = link->next)
+    {
+        GList *new_link;
+
+        /* See if this user is in the current list */
+        for (new_link = priv->users; new_link; new_link = new_link->next)
+        {
+            if (new_link->data == link->data)
+                break;
+        }
+
+        if (!new_link)
+        {
+            LightDMUser *info = link->data;
+            g_debug ("User %s removed", lightdm_user_get_name (info));
+            g_signal_emit (user_list, list_signals[USER_REMOVED], 0, info);
+            g_object_unref (info);
+        }
+    }
+    g_list_free (old_users);
+}
+
+static void
+passwd_changed_cb (GFileMonitor *monitor, GFile *file, GFile *other_file, GFileMonitorEvent event_type, LightDMUserList *user_list)
+{
+    if (event_type == G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT)
+    {
+        g_debug ("%s changed, reloading user list", g_file_get_path (file));
+        load_passwd_file (user_list, TRUE);
+    }
+}
+
+static gboolean
+update_user (UserAccountObject *object)
+{
+    LightDMUserPrivate *priv = GET_USER_PRIVATE (object->user);
+    GVariant *result, *value;
+    GVariantIter *iter;
+    gchar *name;
+    GError *error = NULL;
+
+    result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (object->proxy),
+                                          "org.freedesktop.Accounts",
+                                          g_dbus_proxy_get_object_path (object->proxy),
+                                          "org.freedesktop.DBus.Properties",
+                                          "GetAll",
+                                          g_variant_new ("(s)", "org.freedesktop.Accounts.User"),
+                                          G_VARIANT_TYPE ("(a{sv})"),
+                                          G_DBUS_CALL_FLAGS_NONE,
+                                          -1,
+                                          NULL,
+                                          &error);
+    if (error)
+        g_warning ("Error updating user %s: %s", g_dbus_proxy_get_object_path (object->proxy), error->message);
+    g_clear_error (&error);
+    if (!result)
+        return FALSE;
+
+    g_variant_get (result, "(a{sv})", &iter);
+    while (g_variant_iter_loop (iter, "{&sv}", &name, &value))
+    {
+        if (strcmp (name, "UserName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+        {
+            gchar *user_name;
+            g_variant_get (value, "&s", &user_name);
+            g_free (priv->name);
+            priv->name = g_strdup (user_name);
+        }
+        else if (strcmp (name, "RealName") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+        {
+            gchar *real_name;
+            g_variant_get (value, "&s", &real_name);
+            g_free (priv->real_name);
+            priv->real_name = g_strdup (real_name);
+        }
+        else if (strcmp (name, "HomeDirectory") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+        {
+            gchar *home_directory;
+            g_variant_get (value, "&s", &home_directory);
+            g_free (priv->home_directory);
+            priv->home_directory = g_strdup (home_directory);
+        }
+        else if (strcmp (name, "IconFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+        {
+            gchar *icon_file;
+            g_variant_get (value, "&s", &icon_file);
+            g_free (priv->image);
+            if (strcmp (icon_file, "") == 0)
+                priv->image = NULL;
+            else
+                priv->image = g_strdup (icon_file);
+        }
+        else if (strcmp (name, "BackgroundFile") == 0 && g_variant_is_of_type (value, G_VARIANT_TYPE_STRING))
+        {
+            gchar *background_file;
+            g_variant_get (value, "&s", &background_file);
+            g_free (priv->background);
+            if (strcmp (background_file, "") == 0)
+                priv->background = NULL;
+            else
+                priv->background = g_strdup (background_file);
+        }
+    }
+    g_variant_iter_free (iter);
+
+    g_variant_unref (result);
+
+    return TRUE;
+}
+
+static void
+user_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, UserAccountObject *object)
+{
+    if (strcmp (signal_name, "Changed") == 0)
+    {
+        if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("()")))
+        {
+            g_debug ("User %s changed", g_dbus_proxy_get_object_path (object->proxy));
+            update_user (object);
+            g_signal_emit (object->user, user_signals[CHANGED], 0);
+        }
+        else
+            g_warning ("Got org.freedesktop.Accounts.User signal Changed with unknown parameters %s", g_variant_get_type_string (parameters));
+    }
+}
+
+static UserAccountObject *
+user_account_object_new (LightDMUserList *user_list, const gchar *path)
+{
+    GDBusProxy *proxy;
+    UserAccountObject *object;
+    GError *error = NULL;
+
+    proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                           G_DBUS_PROXY_FLAGS_NONE,
+                                           NULL,
+                                           "org.freedesktop.Accounts",
+                                           path,
+                                           "org.freedesktop.Accounts.User",
+                                           NULL,
+                                           &error);
+    if (error)
+        g_warning ("Error getting user %s: %s", path, error->message);
+    g_clear_error (&error);
+    if (!proxy)
+        return NULL;
+
+    object = g_malloc0 (sizeof (UserAccountObject));  
+    object->user = g_object_new (LIGHTDM_TYPE_USER, NULL);
+    GET_USER_PRIVATE (object->user)->user_list = user_list;
+    object->proxy = proxy;
+    g_signal_connect (proxy, "g-signal", G_CALLBACK (user_signal_cb), object);
+  
+    return object;
+}
+
+static void
+user_account_object_free (UserAccountObject *object)
+{
+    if (!object)
+        return;
+    g_object_unref (object->user);
+    g_object_unref (object->proxy);
+    g_free (object);
+}
+
+static UserAccountObject *
+find_user_account_object (LightDMUserList *user_list, const gchar *path)
+{
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+    GList *link;
+
+    for (link = priv->user_account_objects; link; link = link->next)
+    {
+        UserAccountObject *object = link->data;
+        if (strcmp (g_dbus_proxy_get_object_path (object->proxy), path) == 0)
+            return object;
+    }
+
+    return NULL;
+}
+
+static void
+user_accounts_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
+{
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+  
+    if (strcmp (signal_name, "UserAdded") == 0)
+    {
+        if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+        {
+            gchar *path;
+            UserAccountObject *object;
+
+            g_variant_get (parameters, "(&o)", &path);
+
+            /* Ignore duplicate requests */
+            object = find_user_account_object (user_list, path);
+            if (object)
+                return;
+
+            object = user_account_object_new (user_list, path);
+            if (object && update_user (object))
+            {
+                g_debug ("User %s added", path);
+                priv->user_account_objects = g_list_append (priv->user_account_objects, object);
+                priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
+                g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
+                g_signal_emit (user_list, list_signals[USER_ADDED], 0, object->user);
+            }
+            else
+                user_account_object_free (object);
+        }
+        else
+            g_warning ("Got UserAccounts signal UserAdded with unknown parameters %s", g_variant_get_type_string (parameters));
+    }
+    else if (strcmp (signal_name, "UserDeleted") == 0)
+    {
+        if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+        {
+            gchar *path;
+            UserAccountObject *object;
+
+            g_variant_get (parameters, "(&o)", &path);
+
+            object = find_user_account_object (user_list, path);
+            if (!object)
+                return;
+
+            g_debug ("User %s deleted", path);
+            priv->users = g_list_remove (priv->users, object->user);
+            g_object_unref (object->user);
+
+            g_signal_emit (user_list, list_signals[USER_REMOVED], 0, object->user);
+
+            priv->user_account_objects = g_list_remove (priv->user_account_objects, object);
+            user_account_object_free (object);
+        }
+        else
+            g_warning ("Got UserAccounts signal UserDeleted with unknown parameters %s", g_variant_get_type_string (parameters));
+    }
+}
+
+static Session *
+load_session (LightDMUserList *user_list, const gchar *path)
+{
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+    Session *session = NULL;
+    GVariant *result, *username;
+    GError *error = NULL;
+
+    result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
+                                          "org.freedesktop.DisplayManager",
+                                          path,
+                                          "org.freedesktop.DBus.Properties",
+                                          "Get",
+                                          g_variant_new ("(ss)", "org.freedesktop.DisplayManager.Session", "UserName"),
+                                          G_VARIANT_TYPE ("(v)"),
+                                          G_DBUS_CALL_FLAGS_NONE,
+                                          -1,
+                                          NULL,
+                                          &error);
+    if (error)
+        g_warning ("Error getting UserName from org.freedesktop.DisplayManager.Session: %s", error->message);
+    g_clear_error (&error);
+    if (!result)
+        return NULL;
+
+    g_variant_get (result, "(v)", &username);
+    if (g_variant_is_of_type (username, G_VARIANT_TYPE_STRING))
+    {
+        gchar *name;
+
+        g_variant_get (username, "&s", &name);
+
+        g_debug ("Loaded session %s (%s)", path, name);
+        session = g_object_new (session_get_type (), NULL);
+        session->username = g_strdup (name);
+        session->path = g_strdup (path);
+        priv->sessions = g_list_append (priv->sessions, session);
+    }
+    g_variant_unref (username);
+    g_variant_unref (result);
+
+    return session;
+}
+
+static void
+display_manager_signal_cb (GDBusProxy *proxy, gchar *sender_name, gchar *signal_name, GVariant *parameters, LightDMUserList *user_list)
+{
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+
+    if (strcmp (signal_name, "SessionAdded") == 0)
+    {
+        if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+        {
+            gchar *path;
+            Session *session;
+            LightDMUser *user = NULL;
+
+            g_variant_get (parameters, "(&o)", &path);
+            session = load_session (user_list, path);
+            if (session)
+                user = get_user_by_name (user_list, session->username);
+            if (user)
+                g_signal_emit (user, user_signals[CHANGED], 0);
+        }
+    }
+    else if (strcmp (signal_name, "SessionRemoved") == 0)
+    {
+        if (g_variant_is_of_type (parameters, G_VARIANT_TYPE ("(o)")))
+        {
+            gchar *path;
+            GList *link;
+
+            g_variant_get (parameters, "(&o)", &path);
+
+            for (link = priv->sessions; link; link = link->next)
+            {
+                Session *session = link->data;
+                if (strcmp (session->path, path) == 0)
+                {
+                    LightDMUser *user;
+
+                    g_debug ("Session %s removed", path);
+                    priv->sessions = g_list_remove_link (priv->sessions, link);
+                    user = get_user_by_name (user_list, session->username);
+                    if (user)
+                        g_signal_emit (user, user_signals[CHANGED], 0);
+                    g_object_unref (session);
+                    break;
+                }
+            }
+        }
+    }
+}
+
+static void
+update_users (LightDMUserList *user_list)
+{
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (user_list);
+    GError *error = NULL;
+
+    if (priv->have_users)
+        return;
+    priv->have_users = TRUE;
+
+    priv->accounts_service_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                                  G_DBUS_PROXY_FLAGS_NONE,
+                                                                  NULL,
+                                                                  "org.freedesktop.Accounts",
+                                                                  "/org/freedesktop/Accounts",
+                                                                  "org.freedesktop.Accounts",
+                                                                  NULL,
+                                                                  &error);
+    if (error)
+        g_warning ("Error contacting org.freedesktop.Accounts: %s", error->message);
+    g_clear_error (&error);
+
+    /* Check if the service exists */
+    if (priv->accounts_service_proxy)
+    {
+        gchar *name;
+
+        name = g_dbus_proxy_get_name_owner (priv->accounts_service_proxy);
+        if (!name)
+        {
+            g_debug ("org.freedesktop.Accounts does not exist, falling back to passwd file");
+            g_object_unref (priv->accounts_service_proxy);
+            priv->accounts_service_proxy = NULL;
+        }
+        g_free (name);
+    }
+
+    if (priv->accounts_service_proxy)
+    {
+        GVariant *result;
+
+        g_signal_connect (priv->accounts_service_proxy, "g-signal", G_CALLBACK (user_accounts_signal_cb), user_list);
+
+        result = g_dbus_proxy_call_sync (priv->accounts_service_proxy,
+                                         "ListCachedUsers",
+                                         g_variant_new ("()"),
+                                         G_DBUS_CALL_FLAGS_NONE,
+                                         -1,
+                                         NULL,
+                                         &error);
+        if (error)
+            g_warning ("Error getting user list from org.freedesktop.Accounts: %s", error->message);
+        g_clear_error (&error);
+        if (!result)
+            return;
+
+        if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(ao)")))
+        {
+            GVariantIter *iter;
+            const gchar *path;
+
+            g_debug ("Loading users from org.freedesktop.Accounts");
+            g_variant_get (result, "(ao)", &iter);
+            while (g_variant_iter_loop (iter, "&o", &path))
+            {
+                UserAccountObject *object;
+
+                g_debug ("Loading user %s", path);
+
+                object = user_account_object_new (user_list, path);
+                if (object && update_user (object))
+                {
+                    priv->user_account_objects = g_list_append (priv->user_account_objects, object);
+                    priv->users = g_list_insert_sorted (priv->users, g_object_ref (object->user), compare_user);
+                    g_signal_connect (object->user, "changed", G_CALLBACK (user_changed_cb), user_list);
+                }
+                else
+                    user_account_object_free (object);
+            }
+            g_variant_iter_free (iter);
+        }
+        else
+            g_warning ("Unexpected type from ListCachedUsers: %s", g_variant_get_type_string (result));
+
+        g_variant_unref (result);
+    }
+    else
+    {
+        GFile *passwd_file;
+
+        load_passwd_file (user_list, FALSE);
+
+        /* Watch for changes to user list */
+
+        passwd_file = g_file_new_for_path (PASSWD_FILE);
+        priv->passwd_monitor = g_file_monitor (passwd_file, G_FILE_MONITOR_NONE, NULL, &error);
+        g_object_unref (passwd_file);
+        if (error)
+            g_warning ("Error monitoring %s: %s", PASSWD_FILE, error->message);
+        else
+            g_signal_connect (priv->passwd_monitor, "changed", G_CALLBACK (passwd_changed_cb), user_list);
+        g_clear_error (&error);
+    }
+
+    priv->display_manager_proxy = g_dbus_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+                                                                 G_DBUS_PROXY_FLAGS_NONE,
+                                                                 NULL,
+                                                                 "org.freedesktop.DisplayManager",
+                                                                 "/org/freedesktop/DisplayManager",
+                                                                 "org.freedesktop.DisplayManager",
+                                                                 NULL,
+                                                                 &error);
+    if (error)
+        g_warning ("Error contacting org.freedesktop.DisplayManager: %s", error->message);
+    g_clear_error (&error);
+
+    if (priv->display_manager_proxy)
+    {
+        GVariant *result;
+
+        g_signal_connect (priv->display_manager_proxy, "g-signal", G_CALLBACK (display_manager_signal_cb), user_list);
+
+        result = g_dbus_connection_call_sync (g_dbus_proxy_get_connection (priv->display_manager_proxy),
+                                              "org.freedesktop.DisplayManager",
+                                              "/org/freedesktop/DisplayManager",
+                                              "org.freedesktop.DBus.Properties",
+                                              "Get",
+                                              g_variant_new ("(ss)", "org.freedesktop.DisplayManager", "Sessions"),
+                                              G_VARIANT_TYPE ("(v)"),
+                                              G_DBUS_CALL_FLAGS_NONE,
+                                              -1,
+                                              NULL,
+                                              &error);
+        if (error)
+            g_warning ("Error getting session list from org.freedesktop.DisplayManager: %s", error->message);
+        g_clear_error (&error);
+        if (!result)
+            return;
+
+        if (g_variant_is_of_type (result, G_VARIANT_TYPE ("(v)")))
+        {
+            GVariant *value;
+            GVariantIter *iter;
+            const gchar *path;
+
+            g_variant_get (result, "(v)", &value);
+
+            g_debug ("Loading sessions from org.freedesktop.DisplayManager");
+            g_variant_get (value, "ao", &iter);
+            while (g_variant_iter_loop (iter, "&o", &path))
+                load_session (user_list, path);
+            g_variant_iter_free (iter);
+
+            g_variant_unref (value);
+        }
+        else
+            g_warning ("Unexpected type from org.freedesktop.DisplayManager.Sessions: %s", g_variant_get_type_string (result));
+
+        g_variant_unref (result);
+    }
+}
+
+/**
+ * lightdm_user_list_get_length:
+ * @user_list: a #LightDMUserList
+ *
+ * Return value: The number of users able to log in
+ **/
+gint
+lightdm_user_list_get_length (LightDMUserList *user_list)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), 0);
+    update_users (user_list);
+    return g_list_length (GET_LIST_PRIVATE (user_list)->users);
+}
+
+/**
+ * lightdm_user_list_get_users:
+ * @user_list: A #LightDMUserList
+ *
+ * Get a list of users to present to the user.  This list may be a subset of the
+ * available users and may be empty depending on the server configuration.
+ *
+ * Return value: (element-type LightDMUser) (transfer none): A list of #LightDMUser that should be presented to the user.
+ **/
+GList *
+lightdm_user_list_get_users (LightDMUserList *user_list)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
+    update_users (user_list);
+    return GET_LIST_PRIVATE (user_list)->users;
+}
+
+/**
+ * lightdm_user_list_get_user_by_name:
+ * @user_list: A #LightDMUserList
+ * @username: Name of user to get.
+ *
+ * Get infomation about a given user or #NULL if this user doesn't exist.
+ *
+ * Return value: (transfer none): A #LightDMUser entry for the given user.
+ **/
+LightDMUser *
+lightdm_user_list_get_user_by_name (LightDMUserList *user_list, const gchar *username)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER_LIST (user_list), NULL);
+    g_return_val_if_fail (username != NULL, NULL);
+
+    update_users (user_list);
+
+    return get_user_by_name (user_list, username);
+}
+
+static void
+lightdm_user_list_init (LightDMUserList *user_list)
+{
+}
+
+static void
+lightdm_user_list_set_property (GObject    *object,
+                                guint       prop_id,
+                                const GValue *value,
+                                GParamSpec *pspec)
+{
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_user_list_get_property (GObject    *object,
+                                guint       prop_id,
+                                GValue     *value,
+                                GParamSpec *pspec)
+{
+    LightDMUserList *self;
+
+    self = LIGHTDM_USER_LIST (object);
+
+    switch (prop_id)
+    {
+    case LIST_PROP_NUM_USERS:
+        g_value_set_int (value, lightdm_user_list_get_length (self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+lightdm_user_list_finalize (GObject *object)
+{
+    LightDMUserList *self = LIGHTDM_USER_LIST (object);
+    LightDMUserListPrivate *priv = GET_LIST_PRIVATE (self);
+
+    if (priv->accounts_service_proxy)
+        g_object_unref (priv->accounts_service_proxy);
+    g_list_free_full (priv->user_account_objects, (GDestroyNotify) user_account_object_free);
+    if (priv->passwd_monitor)
+        g_object_unref (priv->passwd_monitor);
+    g_list_free_full (priv->users, g_object_unref);
+    g_list_free_full (priv->sessions, g_object_unref);
+
+    G_OBJECT_CLASS (lightdm_user_list_parent_class)->finalize (object);
+}
+
+static void
+lightdm_user_list_class_init (LightDMUserListClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+    g_type_class_add_private (klass, sizeof (LightDMUserListPrivate));
+
+    object_class->set_property = lightdm_user_list_set_property;
+    object_class->get_property = lightdm_user_list_get_property;
+    object_class->finalize = lightdm_user_list_finalize;
+
+    g_object_class_install_property (object_class,
+                                     LIST_PROP_NUM_USERS,
+                                     g_param_spec_int ("num-users",
+                                                       "num-users",
+                                                       "Number of login users",
+                                                       0, G_MAXINT, 0,
+                                                       G_PARAM_READABLE));
+    /**
+     * LightDMUserList::user-added:
+     * @user_list: A #LightDMUserList
+     * @user: The #LightDM user that has been added.
+     *
+     * The ::user-added signal gets emitted when a user account is created.
+     **/
+    list_signals[USER_ADDED] =
+        g_signal_new ("user-added",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMUserListClass, user_added),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__OBJECT,
+                      G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
+
+    /**
+     * LightDMUserList::user-changed:
+     * @user_list: A #LightDMUserList
+     * @user: The #LightDM user that has been changed.
+     *
+     * The ::user-changed signal gets emitted when a user account is modified.
+     **/
+    list_signals[USER_CHANGED] =
+        g_signal_new ("user-changed",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMUserListClass, user_changed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__OBJECT,
+                      G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
+
+    /**
+     * LightDMUserList::user-removed:
+     * @user_list: A #LightDMUserList
+     * @user: The #LightDM user that has been removed.
+     *
+     * The ::user-removed signal gets emitted when a user account is removed.
+     **/
+    list_signals[USER_REMOVED] =
+        g_signal_new ("user-removed",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMUserListClass, user_removed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__OBJECT,
+                      G_TYPE_NONE, 1, LIGHTDM_TYPE_USER);
+}
+
+/**
+ * lightdm_user_get_name:
+ * @user: A #LightDMUser
+ * 
+ * Get the name of a user.
+ * 
+ * Return value: The name of the given user
+ **/
+const gchar *
+lightdm_user_get_name (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    return GET_USER_PRIVATE (user)->name;
+}
+
+/**
+ * lightdm_user_get_real_name:
+ * @user: A #LightDMUser
+ * 
+ * Get the real name of a user.
+ *
+ * Return value: The real name of the given user
+ **/
+const gchar *
+lightdm_user_get_real_name (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    return GET_USER_PRIVATE (user)->real_name;
+}
+
+/**
+ * lightdm_user_get_display_name:
+ * @user: A #LightDMUser
+ * 
+ * Get the display name of a user.
+ * 
+ * Return value: The display name of the given user
+ **/
+const gchar *
+lightdm_user_get_display_name (LightDMUser *user)
+{
+    LightDMUserPrivate *priv;
+
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+
+    priv = GET_USER_PRIVATE (user);
+    if (strcmp (priv->real_name, ""))
+        return priv->real_name;
+    else
+        return priv->name;
+}
+
+/**
+ * lightdm_user_get_home_directory:
+ * @user: A #LightDMUser
+ * 
+ * Get the home directory for a user.
+ * 
+ * Return value: The users home directory
+ */
+const gchar *
+lightdm_user_get_home_directory (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    return GET_USER_PRIVATE (user)->home_directory;
+}
+
+/**
+ * lightdm_user_get_image:
+ * @user: A #LightDMUser
+ * 
+ * Get the image URI for a user.
+ * 
+ * Return value: The image URI for the given user or #NULL if no URI
+ **/
+const gchar *
+lightdm_user_get_image (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    return GET_USER_PRIVATE (user)->image;
+}
+
+/**
+ * lightdm_user_get_background:
+ * @user: A #LightDMUser
+ * 
+ * Get the background file path for a user.
+ * 
+ * Return value: The background file path for the given user or #NULL if no path
+ **/
+const gchar *
+lightdm_user_get_background (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    return GET_USER_PRIVATE (user)->background;
+}
+
+static void
+load_dmrc (LightDMUser *user)
+{
+    LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+    gchar *path;
+    //gboolean have_dmrc;
+
+    if (!priv->dmrc_file)
+        priv->dmrc_file = g_key_file_new ();
+
+    /* Load from the user directory */  
+    path = g_build_filename (priv->home_directory, ".dmrc", NULL);
+    /*have_dmrc = */g_key_file_load_from_file (priv->dmrc_file, path, G_KEY_FILE_KEEP_COMMENTS, NULL);
+    g_free (path);
+
+    /* If no ~/.dmrc, then load from the cache */
+    // FIXME
+
+    // FIXME: Watch for changes
+
+    /* The Language field is actually a locale, strip the codeset off it to get the language */
+    if (priv->language)
+        g_free (priv->language);
+    priv->language = g_key_file_get_string (priv->dmrc_file, "Desktop", "Language", NULL);
+    if (priv->language)
+    {
+        gchar *codeset = strchr (priv->language, '.');
+        if (codeset)
+            *codeset = '\0';
+    }
+
+    if (priv->layouts)
+    {
+        g_strfreev (priv->layouts);
+        priv->layouts = NULL;
+    }
+    if (g_key_file_has_key (priv->dmrc_file, "Desktop", "Layout", NULL))
+    {
+        priv->layouts = g_malloc (sizeof (gchar *) * 2);
+        priv->layouts[0] = g_key_file_get_string (priv->dmrc_file, "Desktop", "Layout", NULL);
+        priv->layouts[1] = NULL;
+    }
+
+    if (priv->session)
+        g_free (priv->session);
+    priv->session = g_key_file_get_string (priv->dmrc_file, "Desktop", "Session", NULL);
+}
+
+static GVariant *
+get_property (GDBusProxy *proxy, const gchar *property)
+{
+    GVariant *answer;
+
+    if (!proxy)
+        return NULL;
+
+    answer = g_dbus_proxy_get_cached_property (proxy, property);
+
+    if (!answer)
+    {
+        g_warning ("Could not get accounts property %s", property);
+        return NULL;
+    }
+
+    return answer;
+}
+
+static gboolean
+get_boolean_property (GDBusProxy *proxy, const gchar *property)
+{
+    GVariant *answer;
+    gboolean rv;
+
+    answer = get_property (proxy, property);
+    if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_BOOLEAN))
+    {
+        g_warning ("Unexpected accounts property type for %s: %s",
+                   property, g_variant_get_type_string (answer));
+        g_variant_unref (answer);
+        return FALSE;
+    }
+
+    rv = g_variant_get_boolean (answer);
+    g_variant_unref (answer);
+
+    return rv;
+}
+
+static gchar *
+get_string_property (GDBusProxy *proxy, const gchar *property)
+{
+    GVariant *answer;
+    gchar *rv;
+  
+    answer = get_property (proxy, property);
+    if (!g_variant_is_of_type (answer, G_VARIANT_TYPE_STRING))
+    {
+        g_warning ("Unexpected accounts property type for %s: %s",
+                   property, g_variant_get_type_string (answer));
+        g_variant_unref (answer);
+        return NULL;
+    }
+
+    rv = g_strdup (g_variant_get_string (answer, NULL));
+    if (strcmp (rv, "") == 0)
+    {
+        g_free (rv);
+        rv = NULL;
+    }
+    g_variant_unref (answer);
+
+    return rv;
+}
+
+static gchar **
+get_string_array_property (GDBusProxy *proxy, const gchar *property)
+{
+    GVariant *answer;
+    gchar **rv;
+
+    if (!proxy)
+        return NULL;
+
+    answer = g_dbus_proxy_get_cached_property (proxy, property);
+
+    if (!answer)
+    {
+        g_warning ("Could not get accounts property %s", property);
+        return NULL;
+    }
+
+    if (!g_variant_is_of_type (answer, G_VARIANT_TYPE ("as")))
+    {
+        g_warning ("Unexpected accounts property type for %s: %s",
+                   property, g_variant_get_type_string (answer));
+        g_variant_unref (answer);
+        return NULL;
+    }
+
+    rv = g_variant_dup_strv (answer, NULL);
+
+    g_variant_unref (answer);
+    return rv;
+}
+
+static gboolean
+load_accounts_service (LightDMUser *user)
+{
+    LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+    LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
+    UserAccountObject *account = NULL;
+    GList *iter;
+    gchar **value;
+
+    /* First, find AccountObject proxy */
+    for (iter = list_priv->user_account_objects; iter; iter = iter->next)
+    {
+        UserAccountObject *a = iter->data;
+        if (a->user == user)
+        {
+            account = a;
+            break;
+        }
+    }
+    if (!account)
+        return FALSE;
+
+    /* We have proxy, let's grab some properties */
+    if (priv->language)
+        g_free (priv->language);
+    priv->language = get_string_property (account->proxy, "Language");
+    if (priv->session)
+        g_free (priv->session);
+    priv->session = get_string_property (account->proxy, "XSession");
+
+    value = get_string_array_property (account->proxy, "XKeyboardLayouts");
+    if (value)
+    {
+        if (value[0])
+        {
+            g_strfreev (priv->layouts);
+            priv->layouts = value;
+        }
+        else
+            g_strfreev (value);
+    }
+
+    priv->has_messages = get_boolean_property (account->proxy, "XHasMessages");
+
+    return TRUE;
+}
+
+/* Loads language/layout/session info for user */
+static void
+load_user_values (LightDMUser *user)
+{
+    LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+
+    load_dmrc (user);
+    load_accounts_service (user); // overrides dmrc values
+
+    /* Ensure a few guarantees */
+    if (priv->layouts == NULL)
+    {
+        priv->layouts = g_malloc (sizeof (gchar *) * 1);
+        priv->layouts[0] = NULL;
+    }
+}
+
+/**
+ * lightdm_user_get_language:
+ * @user: A #LightDMUser
+ * 
+ * Get the language for a user.
+ * 
+ * Return value: The language for the given user or #NULL if using system defaults.
+ **/
+const gchar *
+lightdm_user_get_language (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    load_user_values (user);
+    return GET_USER_PRIVATE (user)->language;
+}
+
+/**
+ * lightdm_user_get_layout:
+ * @user: A #LightDMUser
+ * 
+ * Get the keyboard layout for a user.
+ * 
+ * Return value: The keyboard layout for the given user or #NULL if using system defaults.  Copy the value if you want to use it long term.
+ **/
+const gchar *
+lightdm_user_get_layout (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    load_user_values (user);
+    return GET_USER_PRIVATE (user)->layouts[0];
+}
+
+/**
+ * lightdm_user_get_layouts:
+ * @user: A #LightDMUser
+ * 
+ * Get the configured keyboard layouts for a user.
+ * 
+ * Return value: (transfer none): A NULL-terminated array of keyboard layouts for the given user.  Copy the values if you want to use them long term.
+ **/
+const gchar * const *
+lightdm_user_get_layouts (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    load_user_values (user);
+    return (const gchar * const *) GET_USER_PRIVATE (user)->layouts;
+}
+
+/**
+ * lightdm_user_get_session:
+ * @user: A #LightDMUser
+ * 
+ * Get the session for a user.
+ * 
+ * Return value: The session for the given user or #NULL if using system defaults.
+ **/
+const gchar *
+lightdm_user_get_session (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), NULL);
+    load_user_values (user);
+    return GET_USER_PRIVATE (user)->session; 
+}
+
+/**
+ * lightdm_user_get_logged_in:
+ * @user: A #LightDMUser
+ * 
+ * Check if a user is logged in.
+ * 
+ * Return value: #TRUE if the user is currently logged in.
+ **/
+gboolean
+lightdm_user_get_logged_in (LightDMUser *user)
+{
+    LightDMUserPrivate *priv = GET_USER_PRIVATE (user);
+    LightDMUserListPrivate *list_priv = GET_LIST_PRIVATE (priv->user_list);
+    GList *link;
+
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
+
+    for (link = list_priv->sessions; link; link = link->next)
+    {
+        Session *session = link->data;
+        if (strcmp (session->username, priv->name) == 0)
+            return TRUE;
+    }
+
+    return FALSE;
+}
+
+/**
+ * lightdm_user_get_has_messages:
+ * @user: A #LightDMUser
+ * 
+ * Check if a user has waiting messages.
+ * 
+ * Return value: #TRUE if the user has waiting messages.
+ **/
+gboolean
+lightdm_user_get_has_messages (LightDMUser *user)
+{
+    g_return_val_if_fail (LIGHTDM_IS_USER (user), FALSE);
+    load_user_values (user);
+    return GET_USER_PRIVATE (user)->has_messages;
+}
+
+static void
+lightdm_user_init (LightDMUser *user)
+{
+}
+
+static void
+lightdm_user_set_property (GObject    *object,
+                           guint       prop_id,
+                           const GValue *value,
+                           GParamSpec *pspec)
+{
+    G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+}
+
+static void
+lightdm_user_get_property (GObject    *object,
+                           guint       prop_id,
+                           GValue     *value,
+                           GParamSpec *pspec)
+{
+    LightDMUser *self;
+
+    self = LIGHTDM_USER (object);
+
+    switch (prop_id)
+    {
+    case USER_PROP_NAME:
+        g_value_set_string (value, lightdm_user_get_name (self));
+        break;
+    case USER_PROP_REAL_NAME:
+        g_value_set_string (value, lightdm_user_get_real_name (self));
+        break;
+    case USER_PROP_DISPLAY_NAME:
+        g_value_set_string (value, lightdm_user_get_display_name (self));
+        break;
+    case USER_PROP_HOME_DIRECTORY:
+        g_value_set_string (value, lightdm_user_get_home_directory (self));
+        break;
+    case USER_PROP_IMAGE:
+        g_value_set_string (value, lightdm_user_get_image (self));
+        break;
+    case USER_PROP_BACKGROUND:
+        g_value_set_string (value, lightdm_user_get_background (self));
+        break;
+    case USER_PROP_LANGUAGE:
+        g_value_set_string (value, lightdm_user_get_language (self));
+        break;
+    case USER_PROP_LAYOUT:
+        g_value_set_string (value, lightdm_user_get_layout (self));
+        break;
+    case USER_PROP_LAYOUTS:
+        g_value_set_boxed (value, g_strdupv ((gchar **) lightdm_user_get_layouts (self)));
+        break;
+    case USER_PROP_SESSION:
+        g_value_set_string (value, lightdm_user_get_session (self));
+        break;
+    case USER_PROP_LOGGED_IN:
+        g_value_set_boolean (value, lightdm_user_get_logged_in (self));
+        break;
+    case USER_PROP_HAS_MESSAGES:
+        g_value_set_boolean (value, lightdm_user_get_has_messages (self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+lightdm_user_finalize (GObject *object)
+{
+    LightDMUser *self = LIGHTDM_USER (object);
+    LightDMUserPrivate *priv = GET_USER_PRIVATE (self);
+
+    g_free (priv->name);
+    g_free (priv->real_name);
+    g_free (priv->home_directory);
+    g_free (priv->image);
+    g_free (priv->background);
+    g_strfreev (priv->layouts);
+    if (priv->dmrc_file)
+        g_key_file_free (priv->dmrc_file);
+}
+
+static void
+lightdm_user_class_init (LightDMUserClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  
+    g_type_class_add_private (klass, sizeof (LightDMUserPrivate));
+
+    object_class->set_property = lightdm_user_set_property;
+    object_class->get_property = lightdm_user_get_property;
+    object_class->finalize = lightdm_user_finalize;
+
+    g_object_class_install_property (object_class,
+                                     USER_PROP_NAME,
+                                     g_param_spec_string ("name",
+                                                          "name",
+                                                          "Username",
+                                                          NULL,
+                                                          G_PARAM_READWRITE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_REAL_NAME,
+                                     g_param_spec_string ("real-name",
+                                                          "real-name",
+                                                          "Users real name",
+                                                          NULL,
+                                                          G_PARAM_READWRITE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_DISPLAY_NAME,
+                                     g_param_spec_string ("display-name",
+                                                          "display-name",
+                                                          "Users display name",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_HOME_DIRECTORY,
+                                     g_param_spec_string ("home-directory",
+                                                          "home-directory",
+                                                          "Home directory",
+                                                          NULL,
+                                                          G_PARAM_READWRITE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_IMAGE,
+                                     g_param_spec_string ("image",
+                                                          "image",
+                                                          "Avatar image",
+                                                          NULL,
+                                                          G_PARAM_READWRITE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_BACKGROUND,
+                                     g_param_spec_string ("background",
+                                                          "background",
+                                                          "User background",
+                                                          NULL,
+                                                          G_PARAM_READWRITE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_LANGUAGE,
+                                     g_param_spec_string ("language",
+                                                         "language",
+                                                         "Language used by this user",
+                                                         NULL,
+                                                         G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_LAYOUT,
+                                     g_param_spec_string ("layout",
+                                                          "layout",
+                                                          "Keyboard layout used by this user",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_LAYOUTS,
+                                     g_param_spec_boxed ("layouts",
+                                                         "layouts",
+                                                         "Keyboard layouts used by this user",
+                                                         G_TYPE_STRV,
+                                                         G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_SESSION,
+                                     g_param_spec_string ("session",
+                                                          "session",
+                                                          "Session used by this user",
+                                                          NULL,
+                                                          G_PARAM_READABLE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_LOGGED_IN,
+                                     g_param_spec_boolean ("logged-in",
+                                                           "logged-in",
+                                                           "TRUE if the user is currently in a session",
+                                                           FALSE,
+                                                           G_PARAM_READWRITE));
+    g_object_class_install_property (object_class,
+                                     USER_PROP_LOGGED_IN,
+                                     g_param_spec_boolean ("has-messages",
+                                                           "has-messages",
+                                                           "TRUE if the user is has waiting messages",
+                                                           FALSE,
+                                                           G_PARAM_READWRITE));
+
+    /**
+     * LightDMUser::changed:
+     * @user: A #LightDMUser
+     *
+     * The ::changed signal gets emitted this user account is modified.
+     **/
+    user_signals[CHANGED] =
+        g_signal_new ("changed",
+                      G_TYPE_FROM_CLASS (klass),
+                      G_SIGNAL_RUN_LAST,
+                      G_STRUCT_OFFSET (LightDMUserClass, changed),
+                      NULL, NULL,
+                      g_cclosure_marshal_VOID__VOID,
+                      G_TYPE_NONE, 0);
+}
+
+static void
+session_init (Session *session)
+{
+}
+
+static void
+session_finalize (GObject *object)
+{
+    Session *self = SESSION (object);
+
+    g_free (self->path);
+    g_free (self->username);
+}
+
+static void
+session_class_init (SessionClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS (klass);
+    object_class->finalize = session_finalize;
+}
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.cpp	(revision 45170)
@@ -0,0 +1,1450 @@
+/* $Id$ */
+/** @file
+ * vbox-greeter - an own LightDM greeter module supporting auto-logons
+ *                controlled by the host.
+ */
+
+/*
+ * Copyright (C) 2012 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+/*******************************************************************************
+*   Header Files                                                               *
+*******************************************************************************/
+#include <pwd.h>
+#include <syslog.h>
+#include <stdlib.h>
+
+#include <lightdm.h>
+#ifdef VBOX_WITH_FLTK
+# include <FL/Fl.H>
+# include <FL/fl_ask.H> /* Yes, the casing is correct for 1.3.0 -- d'oh. */
+# include <FL/Fl_Box.H>
+# include <FL/Fl_Button.H>
+# include <FL/fl_draw.H> /* Same as above. */
+# include <FL/Fl_Double_Window.H>
+# include <FL/Fl_Input.H>
+# include <FL/Fl_Menu_Button.H>
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+#  include <FL/Fl_PNG_Image.H>
+#  include <FL/Fl_Shared_Image.H>
+# endif
+# include <FL/Fl_Secret_Input.H>
+#else
+# include <cairo-xlib.h>
+# include <gtk/gtk.h>
+# include <gdk/gdkx.h>
+#endif
+
+#include <package-generated.h>
+#include "product-generated.h"
+
+#include <iprt/assert.h>
+#include <iprt/buildconfig.h>
+#include <iprt/env.h>
+#include <iprt/file.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/message.h>
+#include <iprt/path.h>
+#include <iprt/process.h>
+#include <iprt/stream.h>
+#include <iprt/system.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+
+#include <VBox/log.h>
+#include <VBox/VBoxGuestLib.h>
+
+/* The greeter's full name for logging. */
+#define VBOX_MODULE_NAME "vbox-lightdm-greeter"
+
+/* UI elements used in this greeter. */
+#define VBOX_GREETER_UI_WND_GREETER     "wnd_greeter"
+
+#define VBOX_GREETER_UI_EDT_USER        "edt_username"
+#define VBOX_GREETER_UI_EDT_PASSWORD    "edt_password"
+#define VBOX_GREETER_UI_BTN_LOGIN       "btn_login"
+#define VBOX_GREETER_UI_LBL_INFO        "lbl_info"
+
+/* UI display options. */
+/** Show the restart menu entry / button. */
+#define VBOX_GREETER_UI_SHOW_RESTART    RT_BIT(0)
+/** Show the shutdown menu entry / button. */
+#define VBOX_GREETER_UI_SHOW_SHUTDOWN   RT_BIT(1)
+/** Show the (customized) top banner.  */
+#define VBOX_GREETER_UI_SHOW_BANNER     RT_BIT(2)
+/** Enable custom colors */
+#define VBOX_GREETER_UI_USE_THEMING     RT_BIT(3)
+
+/** Extracts the 8-bit red component from an uint32_t. */
+#define VBOX_RGB_COLOR_RED(uColor)      uColor & 0xFF
+/** Extracts the 8-bit green component from an uint32_t. */
+#define VBOX_RGB_COLOR_GREEN(uColor)    (uColor >> 8)  & 0xFF
+/** Extracts the 8-bit blue component from an uint32_t. */
+#define VBOX_RGB_COLOR_BLUE(uColor)     (uColor >> 16) & 0xFF
+
+#include <VBox/log.h>
+#ifdef VBOX_WITH_GUEST_PROPS
+ #include <VBox/HostServices/GuestPropertySvc.h>
+ using namespace guestProp;
+#endif
+
+/** The program name (derived from argv[0]). */
+char                *g_pszProgName =  (char *)"";
+/** For debugging. */
+#ifdef DEBUG
+ static int          g_iVerbosity = 99;
+#else
+ static int          g_iVerbosity = 0;
+#endif
+
+/** Logging parameters. */
+/** @todo Make this configurable later. */
+static PRTLOGGER     g_pLoggerRelease = NULL;
+static uint32_t      g_cHistory = 10;                   /* Enable log rotation, 10 files. */
+static uint32_t      g_uHistoryFileTime = RT_SEC_1DAY;  /* Max 1 day per file. */
+static uint64_t      g_uHistoryFileSize = 100 * _1M;    /* Max 100MB per file. */
+
+/**
+ * Context structure which contains all needed
+ * data within callbacks.
+ */
+typedef struct VBOXGREETERCTX
+{
+    /** Pointer to this greeter instance. */
+    LightDMGreeter      *pGreeter;
+#ifdef VBOX_WITH_FLTK
+    Fl_Button           *pBtnLogin;
+    Fl_Input            *pEdtUsername;
+    Fl_Secret_Input     *pEdtPassword;
+    Fl_Box              *pLblInfo;
+#else
+    /** The GTK builder instance for accessing
+     *  the UI elements. */
+    GtkBuilder     *pBuilder;
+#endif
+    /** The timeout (in ms) to wait for credentials. */
+    uint32_t        uTimeoutMS;
+    /** The starting timestamp (in ms) to calculate
+     *  the timeout. */
+    uint64_t        uStartMS;
+    /** Timestamp of last abort message. */
+    uint64_t        uTsAbort;
+    /** The HGCM client ID. */
+    uint32_t        uClientId;
+    /** The credentials. */
+    char           *pszUsername;
+    char           *pszPassword;
+    char           *pszDomain;
+} VBOXGREETERCTX, *PVBOXGREETERCTX;
+
+static void vboxGreeterError(const char *pszFormat, ...)
+{
+    va_list va;
+    char *buf;
+    va_start(va, pszFormat);
+    if (RTStrAPrintfV(&buf, pszFormat, va))
+    {
+        RTLogRelPrintf("%s: error: %s", VBOX_MODULE_NAME, buf);
+        RTStrFree(buf);
+    }
+    va_end(va);
+}
+
+static void vboxGreeterLog(const char *pszFormat, ...)
+{
+    if (g_iVerbosity)
+    {
+        va_list va;
+        char *buf;
+        va_start(va, pszFormat);
+        if (RTStrAPrintfV(&buf, pszFormat, va))
+        {
+            /* Only do normal logging in debug mode; could contain
+             * sensitive data! */
+            RTLogRelPrintf("%s: %s", VBOX_MODULE_NAME, buf);
+            RTStrFree(buf);
+        }
+        va_end(va);
+    }
+}
+
+/** @tood Move the following two functions to VbglR3 (also see pam_vbox). */
+#ifdef VBOX_WITH_GUEST_PROPS
+/**
+ * Reads a guest property.
+ *
+ * @return  IPRT status code.
+ * @param   hPAM                    PAM handle.
+ * @param   uClientID               Guest property service client ID.
+ * @param   pszKey                  Key (name) of guest property to read.
+ * @param   fReadOnly               Indicates whether this key needs to be
+ *                                  checked if it only can be read (and *not* written)
+ *                                  by the guest.
+ * @param   pszValue                Buffer where to store the key's value.
+ * @param   cbValue                 Size of buffer (in bytes).
+ * @param   puTimestamp             Timestamp of the value
+ *                                  retrieved. Optional.
+ */
+static int vbox_read_prop(uint32_t uClientID,
+                          const char *pszKey, bool fReadOnly,
+                          char *pszValue, size_t cbValue, uint64_t *puTimestamp)
+{
+    AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+    AssertPtrReturn(pszValue, VERR_INVALID_POINTER);
+    /* puTimestamp is optional. */
+
+    int rc;
+
+    uint64_t u64Timestamp = 0;
+    char *pszValTemp;
+    char *pszFlags = NULL;
+    /* The buffer for storing the data and its initial size.  We leave a bit
+     * of space here in case the maximum values are raised. */
+    void *pvBuf = NULL;
+    uint32_t cbBuf = MAX_VALUE_LEN + MAX_FLAGS_LEN + _1K;
+
+    /* Because there is a race condition between our reading the size of a
+     * property and the guest updating it, we loop a few times here and
+     * hope.  Actually this should never go wrong, as we are generous
+     * enough with buffer space. */
+    for (unsigned i = 0; i < 10; i++)
+    {
+        void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+        if (pvTmpBuf)
+        {
+            pvBuf = pvTmpBuf;
+            rc = VbglR3GuestPropRead(uClientID, pszKey, pvBuf, cbBuf,
+                                     &pszValTemp, &u64Timestamp, &pszFlags,
+                                     &cbBuf);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+
+        switch (rc)
+        {
+            case VERR_BUFFER_OVERFLOW:
+            {
+                /* Buffer too small, try it with a bigger one next time. */
+                cbBuf += _1K;
+                continue; /* Try next round. */
+            }
+
+            default:
+                break;
+        }
+
+        /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
+        break;
+    }
+
+    if (RT_SUCCESS(rc))
+    {
+        /* Check security bits. */
+        if (pszFlags)
+        {
+            if (fReadOnly
+                && !RTStrStr(pszFlags, "RDONLYGUEST"))
+            {
+                /* If we want a property which is read-only on the guest
+                 * and it is *not* marked as such, deny access! */
+                rc = VERR_ACCESS_DENIED;
+            }
+        }
+        else /* No flags, no access! */
+            rc = VERR_ACCESS_DENIED;
+
+        if (RT_SUCCESS(rc))
+        {
+            /* If everything went well copy property value to our destination buffer. */
+            if (!RTStrPrintf(pszValue, cbValue, "%s", pszValTemp))
+                rc = VERR_BUFFER_OVERFLOW;
+
+            if (puTimestamp)
+                *puTimestamp = u64Timestamp;
+        }
+    }
+
+    return rc;
+}
+
+/**
+ * Waits for a guest property to be changed.
+ *
+ * @return  IPRT status code.
+ * @param   hPAM                    PAM handle.
+ * @param   uClientID               Guest property service client ID.
+ * @param   pszKey                  Key (name) of guest property to wait for.
+ * @param   uTimeoutMS              Timeout (in ms) to wait for the change. Specify
+ *                                  RT_INDEFINITE_WAIT to wait indefinitly.
+ */
+static int vbox_wait_prop(uint32_t uClientID,
+                          const char *pszKey, uint32_t uTimeoutMS)
+{
+    AssertReturn(uClientID, VERR_INVALID_PARAMETER);
+    AssertPtrReturn(pszKey, VERR_INVALID_POINTER);
+
+    int rc;
+
+    /* The buffer for storing the data and its initial size.  We leave a bit
+     * of space here in case the maximum values are raised. */
+    void *pvBuf = NULL;
+    uint32_t cbBuf = MAX_NAME_LEN + MAX_VALUE_LEN + MAX_FLAGS_LEN + _1K;
+
+    for (int i = 0; i < 10; i++)
+    {
+        void *pvTmpBuf = RTMemRealloc(pvBuf, cbBuf);
+        if (pvTmpBuf)
+        {
+            char *pszName = NULL;
+            char *pszValue = NULL;
+            uint64_t u64TimestampOut = 0;
+            char *pszFlags = NULL;
+
+            pvBuf = pvTmpBuf;
+            rc = VbglR3GuestPropWait(uClientID, pszKey, pvBuf, cbBuf,
+                                     0 /* Last timestamp; just wait for next event */, uTimeoutMS,
+                                     &pszName, &pszValue, &u64TimestampOut,
+                                     &pszFlags, &cbBuf);
+        }
+        else
+            rc = VERR_NO_MEMORY;
+
+        if (rc == VERR_BUFFER_OVERFLOW)
+        {
+            /* Buffer too small, try it with a bigger one next time. */
+            cbBuf += _1K;
+            continue; /* Try next round. */
+        }
+
+        /* Everything except VERR_BUFFER_OVERLOW makes us bail out ... */
+        break;
+    }
+
+    return rc;
+}
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+/**
+ * Checks for credentials provided by the host / HGCM.
+ *
+ * @return  IPRT status code. VERR_NOT_FOUND if no credentials are available,
+ *          VINF_SUCCESS on successful retrieval or another IPRT error.
+ * @param   pCtx                    Greeter context.
+ */
+static int vboxGreeterCheckCreds(PVBOXGREETERCTX pCtx)
+{
+    AssertPtrReturn(pCtx, VERR_INVALID_POINTER);
+
+    int rc = VbglR3CredentialsQueryAvailability();
+    if (RT_FAILURE(rc))
+    {
+        if (rc != VERR_NOT_FOUND)
+            vboxGreeterError("vboxGreeterCheckCreds: could not query for credentials! rc=%Rrc. Aborting\n", rc);
+#ifdef DEBUG
+        else
+            vboxGreeterLog("vboxGreeterCheckCreds: no credentials available\n");
+#endif
+    }
+    else
+    {
+        rc = VbglR3CredentialsRetrieve(&pCtx->pszUsername, &pCtx->pszPassword, &pCtx->pszDomain);
+        if (RT_FAILURE(rc))
+        {
+            vboxGreeterError("vboxGreeterCheckCreds: could not retrieve credentials! rc=%Rrc. Aborting\n", rc);
+        }
+        else
+        {
+#ifdef DEBUG
+            vboxGreeterLog("vboxGreeterCheckCreds: credentials retrieved: user=%s, password=%s, domain=%s\n",
+                           pCtx->pszUsername, pCtx->pszPassword, pCtx->pszDomain);
+#else
+            /* Don't log passwords in release mode! */
+            vboxGreeterLog("vboxGreeterCheckCreds: credentials retrieved: user=%s, password=XXX, domain=%s\n",
+                           pCtx->pszUsername, pCtx->pszDomain);
+#endif
+            lightdm_greeter_authenticate(pCtx->pGreeter, pCtx->pszUsername); /* Must be the real user name from host! */
+            /** @todo Add handling domains as well. */
+            /** @todo Move into context destroy! */
+#if 0
+            VbglR3CredentialsDestroy(pszUsername, pszPassword, pszDomain,
+                                     3 /* Three wipe passes */);
+#endif
+        }
+    }
+
+#ifdef DEBUG
+    vboxGreeterLog("vboxGreeterCheckCreds: returned with rc=%Rrc\n", rc);
+#endif
+    return rc;
+}
+
+/**
+ * Called by LightDM when greeter is not needed anymore.
+ *
+ * @param   signum                  Signal number.
+ */
+static void cb_sigterm(int signum)
+{
+    vboxGreeterLog("received SIGTERM (%d), exiting\n", signum);
+
+    exit(RTEXITCODE_SUCCESS);
+}
+
+/**
+ * Callback for showing a user prompt, issued by the LightDM server.
+ *
+ * @param pGreeter                    Pointer to this greeter instance.
+ * @param pszText                     Text to display.
+ * @param enmType                     Type of prompt to display.
+ * @param pvData                      Pointer to user-supplied data.
+ */
+static void cb_lightdm_show_prompt(LightDMGreeter *pGreeter,
+                                   const gchar *pszText, LightDMPromptType enmType,
+                                   gpointer pvData)
+{
+    vboxGreeterLog("cb_lightdm_show_prompt: %s (%d)\n", pszText, enmType);
+
+    PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+    AssertPtr(pCtx);
+
+    switch (enmType)
+    {
+        case 1: /* Password. */
+        {
+            if (pCtx->pszPassword)
+            {
+                lightdm_greeter_respond(pGreeter, pCtx->pszPassword);
+            }
+            else
+            {
+#ifdef VBOX_WITH_FLTK
+                AssertPtr(pCtx->pEdtPassword);
+                const char *pszPwd = pCtx->pEdtPassword->value();
+#else
+                GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, "edt_password"));
+                AssertPtr(pEdtPwd);
+                const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
+#endif
+                lightdm_greeter_respond(pGreeter, pszPwd);
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    VbglR3CredentialsDestroy(pCtx->pszUsername, pCtx->pszPassword, pCtx->pszDomain,
+                             3 /* Three wipe passes */);
+}
+
+/**
+ * Callback for showing a message, issued by the LightDM server.
+ *
+ * @param pGreeter                    Pointer to this greeter instance.
+ * @param pszText                     Text to display.
+ * @param enmType                     Type of message to display.
+ * @param pvData                      Pointer to user-supplied data.
+ */
+static void cb_lightdm_show_message(LightDMGreeter *pGreeter,
+                                    const gchar *pszText, LightDMPromptType enmType,
+                                    gpointer pvData)
+{
+    vboxGreeterLog("cb_lightdm_show_message: %s (%d)\n", pszText, enmType);
+
+    PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+    AssertPtr(pCtx);
+
+#ifdef VBOX_WITH_FLTK
+    AssertPtr(pCtx->pLblInfo);
+    pCtx->pLblInfo->label(pszText);
+#else
+    GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, "lbl_info"));
+    AssertPtr(pLblInfo);
+    gtk_label_set_text(pLblInfo, pszText);
+#endif
+}
+
+/**
+ * Callback for authentication completion, issued by the LightDM server.
+ *
+ * @param pGreeter                    Pointer to this greeter instance.
+ */
+static void cb_lightdm_auth_complete(LightDMGreeter *pGreeter)
+{
+    vboxGreeterLog("cb_lightdm_auth_complete\n");
+
+    const gchar *pszUser = lightdm_greeter_get_authentication_user(pGreeter);
+    vboxGreeterLog("authenticating user: %s\n", pszUser ? pszUser : "<NULL>");
+
+    if (lightdm_greeter_get_is_authenticated(pGreeter))
+    {
+        lightdm_greeter_start_session_sync(pGreeter, NULL, NULL);
+
+        gchar *pszSession = g_strdup(lightdm_greeter_get_default_session_hint(pGreeter));
+        if (pszSession)
+        {
+            vboxGreeterLog("starting session: %s\n", pszSession);
+            if (!lightdm_greeter_start_session_sync(pGreeter, pszSession, NULL))
+            {
+                vboxGreeterError("unable to start session '%s'\n", pszSession);
+            }
+            g_free(pszSession);
+        }
+        else
+            vboxGreeterError("unable to get default session\n");
+    }
+    else
+        vboxGreeterLog("user not authenticated successfully (yet)\n");
+}
+
+/**
+ * Callback for clicking on the "Login" button.
+ *
+ * @param pWidget                     Widget this callback is bound to.
+ * @param pvData                      Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_login(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_login(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+    PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+    AssertPtr(pCtx);
+
+#ifdef VBOX_WITH_FLTK
+    AssertPtr(pCtx->pEdtUsername);
+    const char *pszUser = pCtx->pEdtUsername->value();
+    AssertPtr(pCtx->pEdtPassword);
+    const char *pszPwd = pCtx->pEdtPassword->value();
+#else
+    GtkEntry *pEdtUser = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_USER));
+    AssertPtr(pEdtUser);
+    const gchar *pszUser = gtk_entry_get_text(pEdtUser);
+
+    GtkEntry *pEdtPwd = GTK_ENTRY(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_EDT_PASSWORD));
+    AssertPtr(pEdtPwd);
+    const gchar *pszPwd = gtk_entry_get_text(pEdtPwd);
+#endif
+
+    /** @todo Add domain handling? */
+#ifdef DEBUG
+    vboxGreeterLog("login requested: greeter=%p, user=%s, password=%s\n",
+                   pCtx->pGreeter,
+                   pszUser ? pszUser : "<NONE>",
+                   pszPwd ? pszPwd : "<NONE>");
+#endif
+
+    lightdm_greeter_respond(pCtx->pGreeter, pszPwd);
+    lightdm_greeter_authenticate(pCtx->pGreeter, pszUser);
+}
+
+/**
+ * Callback for clicking on the "Menu" button.
+ *
+ * @param pWidget                     Widget this callback is bound to.
+ * @param pvData                      Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_menu(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_menu(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+
+}
+
+/**
+ * Callback for clicking on the "Restart" button / menu entry.
+ *
+ * @param pWidget                     Widget this callback is bound to.
+ * @param pvData                      Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_restart(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_restart(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+    bool fRestart = true;
+#ifdef VBOX_WITH_FLTK
+    int rc = fl_choice("Really restart the system?", "Yes", "No", NULL);
+    fRestart = rc == 0;
+#endif
+
+    if (fRestart)
+    {
+        vboxGreeterLog("restart requested\n");
+#ifndef DEBUG
+        lightdm_restart(NULL);
+#endif
+    }
+}
+
+/**
+ * Callback for clicking on the "Shutdown" button / menu entry.
+ *
+ * @param pWidget                     Widget this callback is bound to.
+ * @param pvData                      Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+void cb_btn_shutdown(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_btn_shutdown(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+    bool fShutdown = true;
+#ifdef VBOX_WITH_FLTK
+    int rc = fl_choice("Really shutdown the system?", "Yes", "No", NULL);
+    fShutdown = rc == 0;
+#endif
+
+    if (fShutdown)
+    {
+        vboxGreeterLog("shutdown requested\n");
+#ifndef DEBUG
+        lightdm_shutdown(NULL);
+#endif
+    }
+}
+
+#ifdef VBOX_WITH_FLTK
+void cb_edt_username(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_edt_username(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+    vboxGreeterLog("cb_edt_username called\n");
+
+    PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+    AssertPtr(pCtx);
+#ifdef VBOX_WITH_FLTK
+    AssertPtr(pCtx->pEdtPassword);
+    Fl::focus(pCtx->pEdtPassword);
+#endif
+}
+
+#ifdef VBOX_WITH_FLTK
+void cb_edt_password(Fl_Widget *pWidget, void *pvData)
+#else
+void cb_edt_password(GtkWidget *pWidget, gpointer pvData)
+#endif
+{
+    vboxGreeterLog("cb_edt_password called\n");
+
+    PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+    AssertPtr(pCtx);
+#ifdef VBOX_WITH_FLTK
+    AssertPtr(pCtx->pBtnLogin);
+    cb_btn_login(pCtx->pBtnLogin, pvData);
+#endif
+}
+
+/**
+ * Callback for the timer event which is checking for new credentials
+ * from the host.
+ *
+ * @param pvData                      Pointer to user-supplied data.
+ */
+#ifdef VBOX_WITH_FLTK
+static void cb_check_creds(void *pvData)
+#else
+static gboolean cb_check_creds(gpointer pvData)
+#endif
+{
+    PVBOXGREETERCTX pCtx = (PVBOXGREETERCTX)pvData;
+    AssertPtr(pCtx);
+
+#ifdef DEBUG
+    vboxGreeterLog("cb_check_creds called, clientId=%RU32, timeoutMS=%RU32\n",
+                   pCtx->uClientId, pCtx->uTimeoutMS);
+#endif
+
+    int rc = VINF_SUCCESS;
+
+#ifdef VBOX_WITH_GUEST_PROPS
+    bool fAbort = false;
+    char szVal[255];
+    if (pCtx->uClientId)
+    {
+        uint64_t tsAbort;
+        rc = vbox_read_prop(pCtx->uClientId,
+                            "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
+                            true /* Read-only on guest */,
+                            szVal, sizeof(szVal), &tsAbort);
+        switch (rc)
+        {
+            case VINF_SUCCESS:
+#ifdef DEBUG
+                vboxGreeterLog("cb_check_creds: tsAbort %RU64 <-> %RU64\n",
+                               pCtx->uTsAbort, tsAbort);
+#endif
+                if (tsAbort != pCtx->uTsAbort)
+                    fAbort = true; /* Timestamps differs, abort. */
+                pCtx->uTsAbort = tsAbort;
+                break;
+
+            case VERR_TOO_MUCH_DATA:
+                vboxGreeterError("cb_check_creds: temporarily unable to get abort notification\n");
+                break;
+
+            case VERR_NOT_FOUND:
+                /* Value not found, continue checking for credentials. */
+                break;
+
+            default:
+                vboxGreeterError("cb_check_creds: the abort notification request failed with rc=%Rrc\n", rc);
+                fAbort = true; /* Abort on error. */
+                break;
+        }
+    }
+
+    if (fAbort)
+    {
+        /* Get optional message. */
+        int rc2 = vbox_read_prop(pCtx->uClientId,
+                                 "/VirtualBox/GuestAdd/PAM/CredsMsgWaitAbort",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+        if (RT_SUCCESS(rc2))
+        {
+# ifdef VBOX_WITH_FLTK
+            /** @todo */
+# else
+            GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
+            AssertPtr(pLblInfo);
+            gtk_label_set_text(pLblInfo, szVal);
+# endif /* VBOX_WITH_FLTK */
+        }
+
+        vboxGreeterLog("cb_check_creds: got notification from host to abort waiting\n");
+    }
+    else
+    {
+#endif /* VBOX_WITH_GUEST_PROPS */
+        rc = vboxGreeterCheckCreds(pCtx);
+        if (RT_SUCCESS(rc))
+        {
+            /* Credentials retrieved. */
+        }
+        else if (rc == VERR_NOT_FOUND)
+        {
+            /* No credentials found, but try next round (if there's
+             * time left for) ... */
+        }
+#ifdef VBOX_WITH_GUEST_PROPS
+    }
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+    if (rc == VERR_NOT_FOUND) /* No credential found this round. */
+    {
+        /* Calculate timeout value left after process has been started.  */
+        uint64_t u64Elapsed = RTTimeMilliTS() - pCtx->uStartMS;
+        /* Is it time to bail out? */
+        if (pCtx->uTimeoutMS < u64Elapsed)
+        {
+#ifdef VBOX_WITH_GUEST_PROPS
+            int rc2 = vbox_read_prop(pCtx->uClientId,
+                                     "/VirtualBox/GuestAdd/PAM/CredsMsgWaitTimeout",
+                                     true /* Read-only on guest */,
+                                     szVal, sizeof(szVal), NULL /* Timestamp. */);
+            if (RT_SUCCESS(rc2))
+            {
+# ifdef VBOX_WITH_FLTK
+                /** @todo */
+# else
+                GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pCtx->pBuilder, VBOX_GREETER_UI_LBL_INFO));
+                AssertPtr(pLblInfo);
+                gtk_label_set_text(pLblInfo, szVal);
+# endif
+            }
+#endif /* VBOX_WITH_GUEST_PROPS */
+            vboxGreeterLog("cb_check_creds: waiting thread has reached timeout (%dms), exiting ...\n",
+                           pCtx->uTimeoutMS);
+            rc = VERR_TIMEOUT;
+        }
+    }
+
+#ifdef DEBUG
+    vboxGreeterLog("cb_check_creds returned with rc=%Rrc\n", rc);
+#endif
+
+    /* At the moment we only allow *one* shot from the host,
+     * so setting credentials in a second attempt won't be possible
+     * intentionally. */
+
+    if (rc == VERR_NOT_FOUND)
+#ifdef VBOX_WITH_FLTK
+        Fl::repeat_timeout(0.5 /* 500 ms */, cb_check_creds, pvData);
+#else
+        return TRUE; /* No credentials found, do another round. */
+
+    return FALSE; /* Remove timer source on every other error / status. */
+#endif
+}
+
+/**
+ * Release logger callback.
+ *
+ * @return  IPRT status code.
+ * @param   pLoggerRelease
+ * @param   enmPhase
+ * @param   pfnLog
+ */
+static void vboxGreeterLogHeaderFooter(PRTLOGGER pLoggerRelease, RTLOGPHASE enmPhase, PFNRTLOGPHASEMSG pfnLog)
+{
+    /* Some introductory information. */
+    static RTTIMESPEC s_TimeSpec;
+    char szTmp[256];
+    if (enmPhase == RTLOGPHASE_BEGIN)
+        RTTimeNow(&s_TimeSpec);
+    RTTimeSpecToString(&s_TimeSpec, szTmp, sizeof(szTmp));
+
+    switch (enmPhase)
+    {
+        case RTLOGPHASE_BEGIN:
+        {
+            pfnLog(pLoggerRelease,
+                   "vbox-greeter %s r%s (verbosity: %d) %s (%s %s) release log\n"
+                   "Log opened %s\n",
+                   RTBldCfgVersion(), RTBldCfgRevisionStr(), g_iVerbosity, VBOX_BUILD_TARGET,
+                   __DATE__, __TIME__, szTmp);
+
+            int vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp));
+            if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+                pfnLog(pLoggerRelease, "OS Product: %s\n", szTmp);
+            vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp));
+            if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+                pfnLog(pLoggerRelease, "OS Release: %s\n", szTmp);
+            vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp));
+            if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+                pfnLog(pLoggerRelease, "OS Version: %s\n", szTmp);
+            if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW)
+                pfnLog(pLoggerRelease, "OS Service Pack: %s\n", szTmp);
+
+            /* the package type is interesting for Linux distributions */
+            char szExecName[RTPATH_MAX];
+            char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName));
+            pfnLog(pLoggerRelease,
+                   "Executable: %s\n"
+                   "Process ID: %u\n"
+                   "Package type: %s"
+#ifdef VBOX_OSE
+                   " (OSE)"
+#endif
+                   "\n",
+                   pszExecName ? pszExecName : "unknown",
+                   RTProcSelf(),
+                   VBOX_PACKAGE_STRING);
+            break;
+        }
+
+        case RTLOGPHASE_PREROTATE:
+            pfnLog(pLoggerRelease, "Log rotated - Log started %s\n", szTmp);
+            break;
+
+        case RTLOGPHASE_POSTROTATE:
+            pfnLog(pLoggerRelease, "Log continuation - Log started %s\n", szTmp);
+            break;
+
+        case RTLOGPHASE_END:
+            pfnLog(pLoggerRelease, "End of log file - Log started %s\n", szTmp);
+            break;
+
+        default:
+            /* nothing */;
+    }
+}
+
+/**
+ * Creates the default release logger outputting to the specified file.
+ *
+ * @return  IPRT status code.
+ * @param   pszLogFile              Filename for log output.  Optional.
+ */
+static int vboxGreeterLogCreate(const char *pszLogFile)
+{
+    /* Create release logger (stdout + file). */
+    static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES;
+    RTUINT fFlags = RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG;
+#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)
+    fFlags |= RTLOGFLAGS_USECRLF;
+#endif
+    char szError[RTPATH_MAX + 128] = "";
+    int rc = RTLogCreateEx(&g_pLoggerRelease, fFlags, "all",
+                           "VBOXGREETER_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups,
+                           RTLOGDEST_STDOUT,
+                           vboxGreeterLogHeaderFooter, g_cHistory, g_uHistoryFileSize, g_uHistoryFileTime,
+                           szError, sizeof(szError), pszLogFile);
+    if (RT_SUCCESS(rc))
+    {
+        /* register this logger as the release logger */
+        RTLogRelSetDefaultInstance(g_pLoggerRelease);
+
+        /* Explicitly flush the log in case of VBOXGREETER_RELEASE_LOG_FLAGS=buffered. */
+        RTLogFlush(g_pLoggerRelease);
+    }
+
+    return rc;
+}
+
+static void vboxGreeterLogDestroy(void)
+{
+    RTLogDestroy(RTLogRelSetDefaultInstance(NULL));
+}
+
+static int vboxGreeterUsage(void)
+{
+    RTPrintf("Usage:\n"
+             " %-12s [-h|-?|--help] [-F|--logfile <file>]\n"
+             "              [-v|--verbose] [-V|--version]\n", g_pszProgName);
+
+    RTPrintf("\n"
+             " Copyright (C) 2012-" VBOX_C_YEAR " " VBOX_VENDOR "\n");
+
+    return RTEXITCODE_SYNTAX;
+}
+
+int main(int argc, char **argv)
+{
+    int rc = RTR3InitExe(argc, &argv, 0);
+    if (RT_FAILURE(rc))
+        return RTMsgInitFailure(rc);
+    g_pszProgName = RTPathFilename(argv[0]);
+
+    static const RTGETOPTDEF s_aOptions[] =
+    {
+        { "--logfile", 'F', RTGETOPT_REQ_STRING },
+        { "--verbose", 'v', RTGETOPT_REQ_NOTHING },
+        { "--version", 'V', RTGETOPT_REQ_NOTHING }
+    };
+
+    char szLogFile[RTPATH_MAX + 128] = "";
+
+    int ch;
+    RTGETOPTUNION ValueUnion;
+    RTGETOPTSTATE GetState;
+    RTGetOptInit(&GetState, argc, argv,
+                 s_aOptions, RT_ELEMENTS(s_aOptions),
+                 1 /*iFirst*/, RTGETOPTINIT_FLAGS_OPTS_FIRST);
+
+    while (   (ch = RTGetOpt(&GetState, &ValueUnion))
+           && RT_SUCCESS(rc))
+    {
+        /* For options that require an argument, ValueUnion has received the value. */
+        switch (ch)
+        {
+            case 'F':
+                if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", ValueUnion.psz))
+                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to get prepare log file name");
+                break;
+
+            case 'h':
+            case '?':
+                return vboxGreeterUsage();
+
+            case 'v': /* Raise verbosity. */
+                g_iVerbosity++;
+                break;
+
+            case 'V': /* Print version and exit. */
+                RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
+                return RTEXITCODE_SUCCESS;
+                break; /* Never reached. */
+
+            default:
+                return RTGetOptPrintError(ch, &ValueUnion);
+        }
+    }
+
+    if (RT_FAILURE(rc))
+        return RTEXITCODE_SYNTAX;
+
+    rc = VbglR3InitUser();
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to init Vbgl (%Rrc)", rc);
+
+    rc = vboxGreeterLogCreate(strlen(szLogFile) ? szLogFile : NULL);
+    if (RT_FAILURE(rc))
+        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to create release log (%s, %Rrc)",
+                              strlen(szLogFile) ? szLogFile : "<None>", rc);
+
+    vboxGreeterLog("init\n");
+
+    signal(SIGTERM, cb_sigterm);
+
+    /** @todo This function already is too long. Move code into
+     *        functions. */
+
+    VBOXGREETERCTX ctx;
+    RT_ZERO(ctx);
+
+    /* UI parameters. */
+    uint32_t uBgColor = 0; /* The background color. */
+    uint32_t uLogonDlgHdrColor = 0;
+    uint32_t uLogonDlgBgColor = 0; /* The greeter's dialog color. */
+    uint32_t uLogonDlgBtnColor = 0; /* The greeter's button color. */
+
+    char szBannerPath[RTPATH_MAX];
+
+    /* By default most UI elements are shown. */
+    uint32_t uOptsUI = VBOX_GREETER_UI_SHOW_RESTART
+                     | VBOX_GREETER_UI_SHOW_SHUTDOWN;
+#ifdef VBOX_WITH_GUEST_PROPS
+    uint32_t uClientId;
+    rc = VbglR3GuestPropConnect(&uClientId);
+    if (RT_SUCCESS(rc))
+    {
+        vboxGreeterLog("clientId=%RU32\n", uClientId);
+
+        ctx.uClientId = uClientId;
+
+        char szVal[256];
+        int rc2 = vbox_read_prop(uClientId,
+                                 "/VirtualBox/GuestAdd/Greeter/HideRestart",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+        if (   RT_SUCCESS(rc2)
+            && !RTStrICmp(szVal, "1"))
+        {
+            uOptsUI &= ~VBOX_GREETER_UI_SHOW_RESTART;
+        }
+
+        rc2 = vbox_read_prop(uClientId,
+                             "/VirtualBox/GuestAdd/Greeter/HideShutdown",
+                             true /* Read-only on guest */,
+                             szVal, sizeof(szVal), NULL /* Timestamp. */);
+        if (   RT_SUCCESS(rc2)
+            && !RTStrICmp(szVal, "1"))
+        {
+            uOptsUI &= ~VBOX_GREETER_UI_SHOW_SHUTDOWN;
+        }
+
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+        /* Load the banner. */
+        rc2 = vbox_read_prop(uClientId,
+                             "/VirtualBox/GuestAdd/Greeter/BannerPath",
+                             true /* Read-only on guest */,
+                             szBannerPath, sizeof(szBannerPath), NULL /* Timestamp. */);
+        if (RT_SUCCESS(rc2))
+        {
+            if (RTFileExists(szBannerPath))
+            {
+                vboxGreeterLog("showing banner from '%s'\n", szBannerPath);
+                uOptsUI |= VBOX_GREETER_UI_SHOW_BANNER;
+            }
+            else
+                vboxGreeterLog("warning: unable to find banner at '%s', skipping\n", szBannerPath);
+        }
+# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
+
+        /* Use theming?. */
+        rc2 = vbox_read_prop(uClientId,
+                             "/VirtualBox/GuestAdd/Greeter/UseTheming",
+                             true /* Read-only on guest */,
+                             szVal, sizeof(szVal), NULL /* Timestamp. */);
+        if (   RT_SUCCESS(rc2)
+            && !RTStrICmp(szVal, "1"))
+        {
+            vboxGreeterLog("custom theming enabled\n");
+                           uOptsUI |= VBOX_GREETER_UI_USE_THEMING;
+        }
+
+        if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+        {
+            /* Get background color. */
+            rc2 = vbox_read_prop(uClientId,
+                                 "/VirtualBox/GuestAdd/Greeter/Theme/BackgroundColor",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+            if (RT_SUCCESS(rc2))
+            {
+                uBgColor = strtol(szVal, NULL,
+                                  /* Change conversion base when having a 0x prefix. */
+                                  RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+            }
+
+            /* Logon dialog. */
+
+            /* Get header color. */
+            rc2 = vbox_read_prop(uClientId,
+                                 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/HeaderColor",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+            if (RT_SUCCESS(rc2))
+            {
+                uLogonDlgHdrColor = strtol(szVal, NULL,
+                                           /* Change conversion base when having a 0x prefix. */
+                                           RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+            }
+
+            /* Get dialog color. */
+            rc2 = vbox_read_prop(uClientId,
+                                 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/BackgroundColor",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+            if (RT_SUCCESS(rc2))
+            {
+                uLogonDlgBgColor = strtol(szVal, NULL,
+                                          /* Change conversion base when having a 0x prefix. */
+                                          RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+            }
+
+            /* Get button color. */
+            rc2 = vbox_read_prop(uClientId,
+                                 "/VirtualBox/GuestAdd/Greeter/Theme/LogonDialog/ButtonColor",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+            if (RT_SUCCESS(rc2))
+            {
+                uLogonDlgBtnColor = strtol(szVal, NULL,
+                                           /* Change conversion base when having a 0x prefix. */
+                                           RTStrStr(szVal, "0x") == szVal ? 0 : 16);
+            }
+        }
+    }
+#endif
+    vboxGreeterLog("UI options are: %RU32\n", uOptsUI);
+
+#ifdef VBOX_WITH_FLTK
+    int rc2 = Fl::scheme("plastic");
+    if (!rc2)
+        vboxGreeterLog("warning: unable to set visual scheme\n");
+
+    Fl::visual(FL_DOUBLE | FL_INDEX);
+    Fl_Double_Window *pWndMain = new Fl_Double_Window(Fl::w(), Fl::h(), "VirtualBox Guest Additions");
+    AssertPtr(pWndMain);
+    if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+        pWndMain->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uBgColor),
+                                     VBOX_RGB_COLOR_GREEN(uBgColor),
+                                     VBOX_RGB_COLOR_BLUE(uBgColor)));
+    else /* Default colors. */
+        pWndMain->color(fl_rgb_color(0x73, 0x7F, 0x8C));
+
+    Fl_Double_Window *pWndGreeter = new Fl_Double_Window(500, 350);
+    AssertPtr(pWndGreeter);
+    pWndGreeter->set_modal();
+    if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+        pWndGreeter->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBgColor),
+                                        VBOX_RGB_COLOR_GREEN(uLogonDlgBgColor),
+                                        VBOX_RGB_COLOR_BLUE(uLogonDlgBgColor)));
+    else /* Default colors. */
+        pWndGreeter->color(fl_rgb_color(255, 255, 255));
+
+    uint32_t uOffsetX = 130;
+    /**
+     * For now we're using a simple Y offset for moving all elements
+     * down if a banner needs to be shown on top of the greeter. Not
+     * very clean but does the job. Use some more layouting stuff
+     * when this gets more complex.
+     */
+    uint32_t uOffsetY = 80;
+
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+    fl_register_images();
+
+    /** @todo Add basic image type detection based on file
+     *        extension. */
+
+    Fl_PNG_Image *pImgBanner = NULL;
+    if (uOptsUI & VBOX_GREETER_UI_SHOW_BANNER)
+    {
+        pImgBanner = new Fl_PNG_Image(szBannerPath);
+        AssertPtr(pImgBanner);
+
+        /** @todo Make the banner size configurable via guest
+         *        properties. For now it's hardcoded to 460 x 90px. */
+        Fl_Box *pBoxBanner = new Fl_Box(20, uOffsetY, 460, 90, "");
+        AssertPtr(pBoxBanner);
+        pBoxBanner->image(pImgBanner);
+
+        uOffsetY = 120;
+    }
+# endif
+
+    Fl_Box *pLblHeader = new Fl_Box(FL_NO_BOX, 242, uOffsetY, 300, 20,
+                                    "Desktop Login");
+    AssertPtr(pLblHeader);
+
+    /** Note to use an own font:
+     *  Fl_Font myfnt = FL_FREE_FONT + 1;
+     *  Fl::set_font(myfnt, "MyFont"); */
+    Fl_Font fntHeader = FL_FREE_FONT;
+    Fl::set_font(fntHeader, "Courier");
+
+    pLblHeader->align(FL_ALIGN_LEFT);
+    pLblHeader->labelfont(FL_BOLD);
+    pLblHeader->labelsize(24);
+    if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+        pLblHeader->labelcolor(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgHdrColor),
+                                            VBOX_RGB_COLOR_GREEN(uLogonDlgHdrColor),
+                                            VBOX_RGB_COLOR_BLUE(uLogonDlgHdrColor)));
+    else /* Default color. */
+        pLblHeader->labelcolor(fl_rgb_color(0x51, 0x5F, 0x77));
+    uOffsetY += 40;
+
+    /** @todo Add basic NLS support. */
+
+    Fl_Input *pEdtUsername = new Fl_Input(uOffsetX, uOffsetY,
+                                          300, 20, "User Name");
+    AssertPtr(pEdtUsername);
+    pEdtUsername->callback(cb_edt_username, &ctx);
+    pEdtUsername->when(FL_WHEN_ENTER_KEY_ALWAYS);
+    Fl::focus(pEdtUsername);
+    ctx.pEdtUsername = pEdtUsername;
+
+    Fl_Secret_Input *pEdtPassword = new Fl_Secret_Input(uOffsetX, uOffsetY + 40,
+                                                        300, 20, "Password");
+    AssertPtr(pEdtPassword);
+    pEdtPassword->callback(cb_edt_password, &ctx);
+    pEdtPassword->when(FL_WHEN_ENTER_KEY_ALWAYS);
+    ctx.pEdtPassword = pEdtPassword;
+
+    Fl_Button *pBtnLogin = new Fl_Button(uOffsetX, uOffsetY + 70,
+                                         100, 40, "Log In");
+    AssertPtr(pBtnLogin);
+    pBtnLogin->callback(cb_btn_login, &ctx);
+    if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+        pBtnLogin->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
+                                      VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
+                                      VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
+    else /* Default color. */
+        pBtnLogin->color(fl_rgb_color(255, 255, 255));
+    ctx.pBtnLogin = pBtnLogin;
+
+    Fl_Menu_Button *pBtnMenu = new Fl_Menu_Button(uOffsetX + 120, uOffsetY + 70,
+                                                  100, 40, "Options");
+    AssertPtr(pBtnMenu);
+    pBtnMenu->callback(cb_btn_menu, &ctx);
+    if (uOptsUI & VBOX_GREETER_UI_USE_THEMING)
+        pBtnMenu->color(fl_rgb_color(VBOX_RGB_COLOR_RED(uLogonDlgBtnColor),
+                                     VBOX_RGB_COLOR_GREEN(uLogonDlgBtnColor),
+                                     VBOX_RGB_COLOR_BLUE(uLogonDlgBtnColor)));
+    else /* Default color. */
+        pBtnMenu->color(fl_rgb_color(255, 255, 255));
+
+    if (uOptsUI & VBOX_GREETER_UI_SHOW_RESTART)
+        pBtnMenu->add("Restart", "" /* Shortcut */, cb_btn_restart, &ctx, 0 /* Flags */);
+    if (uOptsUI & VBOX_GREETER_UI_SHOW_SHUTDOWN)
+        pBtnMenu->add("Shutdown", "" /* Shortcut */, cb_btn_shutdown, &ctx, 0 /* Flags */);
+
+    char szLabel[255];
+    RTStrPrintf(szLabel, sizeof(szLabel), "Oracle VM VirtualBox Guest Additions %sr%s",
+                RTBldCfgVersion(), RTBldCfgRevisionStr());
+    Fl_Box *pLblInfo = new Fl_Box(FL_NO_BOX, 90, uOffsetY + 150,
+                                  300, 20, szLabel);
+    AssertPtr(pLblInfo);
+    ctx.pLblInfo = pLblInfo;
+
+    pWndGreeter->end();
+    pWndGreeter->position((Fl::w() - pWndGreeter->w()) / 2,
+                          (Fl::h() - pWndGreeter->h()) / 2);
+
+    pWndMain->fullscreen();
+    pWndMain->show(argc, argv);
+    pWndMain->end();
+
+    pWndGreeter->show();
+#else
+    gtk_init(&argc, &argv);
+
+    /* Set default cursor */
+    gdk_window_set_cursor(gdk_get_default_root_window(), gdk_cursor_new(GDK_LEFT_PTR));
+
+    GError *pError = NULL;
+    GtkBuilder *pBuilder = gtk_builder_new();
+    AssertPtr(pBuilder);
+    if (!gtk_builder_add_from_file(pBuilder, "/usr/share/xgreeters/vbox-greeter.ui", &pError))
+    {
+        AssertPtr(pError);
+        vboxGreeterError("unable to load UI: %s", pError->message);
+        return RTEXITCODE_FAILURE;
+    }
+
+    GtkWindow *pWndGreeter = GTK_WINDOW(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_WND_GREETER));
+    AssertPtr(pWndGreeter);
+    GtkButton *pBtnLogin = GTK_BUTTON(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_BTN_LOGIN));
+    AssertPtr(pBtnLogin);
+    GtkLabel *pLblInfo = GTK_LABEL(gtk_builder_get_object(pBuilder, VBOX_GREETER_UI_LBL_INFO));
+    AssertPtr(pLblInfo);
+
+    ctx.pBuilder = pBuilder;
+
+    g_signal_connect(G_OBJECT(pBtnLogin), "clicked", G_CALLBACK(cb_btn_login), &ctx);
+
+    GdkRectangle rectScreen;
+    gdk_screen_get_monitor_geometry(gdk_screen_get_default(), gdk_screen_get_primary_monitor(gdk_screen_get_default()), &rectScreen);
+    vboxGreeterLog("monitor (default) is %dx%d\n", rectScreen.width, rectScreen.height);
+
+    gint iWndX, iWndY;
+    gtk_window_get_default_size(pWndGreeter, &iWndX, &iWndY);
+    vboxGreeterLog("greeter is %dx%d\n", iWndX, iWndY);
+
+    gtk_window_move(pWndGreeter,
+                    (rectScreen.width / 2)  - (iWndX / 2),
+                    (rectScreen.height / 2) - (iWndY / 2));
+    gtk_widget_show(GTK_WIDGET(pWndGreeter));
+
+    g_clear_error(&pError);
+#endif
+
+    /* GType is needed in any case (for LightDM), whether we
+     * use GTK3 or not. */
+    g_type_init();
+
+    GMainLoop *pMainLoop = g_main_loop_new(NULL, FALSE /* Not yet running */);
+    AssertPtr(pMainLoop);
+
+    LightDMGreeter *pGreeter = lightdm_greeter_new();
+    AssertPtr(pGreeter);
+
+    g_signal_connect(pGreeter, "show-prompt", G_CALLBACK(cb_lightdm_show_prompt), &ctx);
+    g_signal_connect(pGreeter, "show-message", G_CALLBACK(cb_lightdm_show_message), &ctx);
+    g_signal_connect(pGreeter, "authentication-complete", G_CALLBACK(cb_lightdm_auth_complete), &ctx);
+
+    ctx.pGreeter = pGreeter;
+
+    if (!lightdm_greeter_connect_sync(pGreeter, NULL))
+    {
+        vboxGreeterError("unable to connect to LightDM server, aborting\n");
+        return RTEXITCODE_FAILURE;
+    }
+
+    vboxGreeterLog("connected to LightDM server\n");
+
+#ifdef VBOX_WITH_GUEST_PROPS
+    bool fCheckCreds = false;
+    if (uClientId)
+    {
+        char szVal[256];
+        rc = vbox_read_prop(uClientId,
+                            "/VirtualBox/GuestAdd/PAM/CredsWait",
+                            true /* Read-only on guest */,
+                            szVal, sizeof(szVal), NULL /* Timestamp. */);
+        if (RT_SUCCESS(rc))
+        {
+            /* All calls which are checked against rc2 are not critical, e.g. it does
+             * not matter if they succeed or not. */
+            uint32_t uTimeoutMS = RT_INDEFINITE_WAIT; /* Wait infinite by default. */
+            rc2 = vbox_read_prop(uClientId,
+                                 "/VirtualBox/GuestAdd/PAM/CredsWaitTimeout",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+            if (RT_SUCCESS(rc2))
+            {
+                uTimeoutMS = RTStrToUInt32(szVal);
+                if (!uTimeoutMS)
+                {
+                    vboxGreeterError("pam_vbox_authenticate: invalid waiting timeout value specified, defaulting to infinite timeout\n");
+                    uTimeoutMS = RT_INDEFINITE_WAIT;
+                }
+                else
+                    uTimeoutMS = uTimeoutMS * 1000; /* Make ms out of s. */
+            }
+
+            ctx.uTimeoutMS = uTimeoutMS;
+
+            rc2 = vbox_read_prop(uClientId,
+                                 "/VirtualBox/GuestAdd/PAM/CredsMsgWaiting",
+                                 true /* Read-only on guest */,
+                                 szVal, sizeof(szVal), NULL /* Timestamp. */);
+            if (RT_SUCCESS(rc2))
+#ifdef VBOX_WITH_FLTK
+                pLblInfo->label(szVal);
+#else
+                gtk_label_set_text(pLblInfo, szVal);
+#endif
+            /* Get initial timestamp so that we can compare the time
+             * whether the value has been changed or not in our event callback. */
+            vbox_read_prop(uClientId,
+                           "/VirtualBox/GuestAdd/PAM/CredsWaitAbort",
+                           true /* Read-only on guest */,
+                           szVal, sizeof(szVal), &ctx.uTsAbort);
+
+            if (RT_SUCCESS(rc))
+            {
+                /* Before we actuall wait for credentials just make sure we didn't already get credentials
+                 * set so that we can skip waiting for them ... */
+                rc2 = vboxGreeterCheckCreds(&ctx);
+                if (rc2 == VERR_NOT_FOUND)
+                {
+                    /* Get current time stamp to later calculate rest of timeout left. */
+                    ctx.uStartMS = RTTimeMilliTS();
+
+                    fCheckCreds = true;
+                }
+            }
+        }
+
+        /* Start the timer to check credentials availability. */
+        if (fCheckCreds)
+#ifdef VBOX_WITH_FLTK
+            Fl::add_timeout(0.5 /* 500 ms */, cb_check_creds, &ctx);
+#else
+            g_timeout_add(500 /* ms */, (GSourceFunc)cb_check_creds, &ctx);
+#endif
+    }
+#endif /* VBOX_WITH_GUEST_PROPS */
+
+#ifdef VBOX_WITH_FLTK
+    /*
+     * Do own GDK main loop processing because FLTK also needs
+     * to have the chance of processing its events.
+     */
+    GMainContext *pMainCtx = g_main_context_default();
+    AssertPtr(pMainCtx);
+
+    for (;;)
+    {
+        g_main_context_iteration(pMainCtx,
+                                 FALSE /* No blocking */);
+        Fl::check();
+    }
+
+    g_main_context_unref(pMainCtx);
+
+# ifdef VBOX_GREETER_WITH_PNG_SUPPORT
+    if (pImgBanner)
+    {
+        delete pImgBanner; /* Call destructor to free bitmap data. */
+        pImgBanner = NULL;
+    }
+# endif /* VBOX_GREETER_WITH_PNG_SUPPORT */
+#else
+    gtk_main();
+#endif
+
+    vboxGreeterLog("terminating\n");
+
+    rc2 = VbglR3GuestPropDisconnect(uClientId);
+    AssertRC(rc2);
+
+    VbglR3Term();
+
+    vboxGreeterLog("terminated with rc=%Rrc\n", rc);
+
+    vboxGreeterLogDestroy();
+
+    return RT_SUCCESS(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
+}
+
+#ifdef DEBUG
+DECLEXPORT(void) RTAssertMsg1Weak(const char *pszExpr, unsigned uLine, const char *pszFile, const char *pszFunction)
+{
+    RTAssertMsg1(pszExpr, uLine, pszFile, pszFunction);
+}
+#endif
+
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.desktop
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.desktop	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.desktop	(revision 45170)
@@ -0,0 +1,5 @@
+[Desktop Entry]
+Name=VirtualBox Greeter
+Comment=Provides Single Sign-On (SSO) support
+Exec=/usr/sbin/vbox-greeter
+Type=Application
Index: /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.ui
===================================================================
--- /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.ui	(revision 45170)
+++ /trunk/src/VBox/Additions/linux/lightdm-greeter/vbox-greeter.ui	(revision 45170)
@@ -0,0 +1,227 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<interface>
+  <!-- interface-requires gtk+ 3.0 -->
+  <object class="GtkWindow" id="wnd_greeter">
+    <property name="width_request">512</property>
+    <property name="height_request">348</property>
+    <property name="can_focus">False</property>
+    <property name="title" translatable="yes">VirtualBox Guest Additions Login</property>
+    <property name="resizable">False</property>
+    <property name="modal">True</property>
+    <property name="window_position">center</property>
+    <property name="default_width">512</property>
+    <property name="default_height">348</property>
+    <property name="has_resize_grip">False</property>
+    <child>
+      <object class="GtkNotebook" id="nb_greeter">
+        <property name="visible">True</property>
+        <property name="can_focus">True</property>
+        <child>
+          <object class="GtkBox" id="box1">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="orientation">vertical</property>
+            <child>
+              <object class="GtkDrawingArea" id="drawingarea1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+              </object>
+              <packing>
+                <property name="expand">True</property>
+                <property name="fill">True</property>
+                <property name="position">0</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkBox" id="box3">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="orientation">vertical</property>
+                <child>
+                  <object class="GtkAspectFrame" id="aspectframe3">
+                    <property name="visible">True</property>
+                    <property name="can_focus">False</property>
+                    <property name="valign">center</property>
+                    <property name="label_xalign">0</property>
+                    <property name="shadow_type">none</property>
+                    <child>
+                      <object class="GtkGrid" id="grid2">
+                        <property name="visible">True</property>
+                        <property name="can_focus">False</property>
+                        <property name="n_columns">2</property>
+                        <child>
+                          <object class="GtkEntry" id="edt_password">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="visibility">False</property>
+                            <property name="invisible_char">●</property>
+                            <property name="activates_default">True</property>
+                            <property name="invisible_char_set">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">1</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkEntry" id="edt_username">
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="invisible_char">●</property>
+                            <property name="invisible_char_set">True</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">0</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label4">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_right">20</property>
+                            <property name="xalign">0</property>
+                            <property name="ypad">10</property>
+                            <property name="label" translatable="yes">Username</property>
+                            <property name="justify">center</property>
+                            <attributes>
+                              <attribute name="weight" value="ultraheavy"/>
+                            </attributes>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">0</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkLabel" id="label5">
+                            <property name="visible">True</property>
+                            <property name="can_focus">False</property>
+                            <property name="margin_right">20</property>
+                            <property name="xalign">0</property>
+                            <property name="ypad">10</property>
+                            <property name="label" translatable="yes">Password</property>
+                            <attributes>
+                              <attribute name="weight" value="ultraheavy"/>
+                            </attributes>
+                          </object>
+                          <packing>
+                            <property name="left_attach">0</property>
+                            <property name="top_attach">1</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <object class="GtkButton" id="btn_login">
+                            <property name="label" translatable="yes">Login</property>
+                            <property name="visible">True</property>
+                            <property name="can_focus">True</property>
+                            <property name="receives_default">True</property>
+                            <property name="use_action_appearance">False</property>
+                          </object>
+                          <packing>
+                            <property name="left_attach">1</property>
+                            <property name="top_attach">2</property>
+                            <property name="width">1</property>
+                            <property name="height">1</property>
+                          </packing>
+                        </child>
+                        <child>
+                          <placeholder/>
+                        </child>
+                      </object>
+                    </child>
+                  </object>
+                  <packing>
+                    <property name="expand">False</property>
+                    <property name="fill">True</property>
+                    <property name="position">0</property>
+                  </packing>
+                </child>
+                <child>
+                  <placeholder/>
+                </child>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">True</property>
+                <property name="position">1</property>
+              </packing>
+            </child>
+            <child>
+              <object class="GtkLabel" id="lbl_info">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="halign">center</property>
+                <property name="valign">end</property>
+                <property name="label" translatable="yes">VirtualBox Guest Additions</property>
+              </object>
+              <packing>
+                <property name="expand">False</property>
+                <property name="fill">False</property>
+                <property name="position">2</property>
+              </packing>
+            </child>
+          </object>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="lblLogin">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Login</property>
+          </object>
+          <packing>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <object class="GtkAspectFrame" id="aspectframe2">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label_xalign">0</property>
+            <property name="shadow_type">none</property>
+            <child>
+              <object class="GtkLabel" id="label1">
+                <property name="visible">True</property>
+                <property name="can_focus">False</property>
+                <property name="label" translatable="yes">VirtualBox Guest Additions Auto-Logon</property>
+                <property name="justify">center</property>
+                <attributes>
+                  <attribute name="weight" value="ultraheavy"/>
+                </attributes>
+              </object>
+            </child>
+          </object>
+          <packing>
+            <property name="position">1</property>
+          </packing>
+        </child>
+        <child type="tab">
+          <object class="GtkLabel" id="lblInformation">
+            <property name="visible">True</property>
+            <property name="can_focus">False</property>
+            <property name="label" translatable="yes">Information</property>
+          </object>
+          <packing>
+            <property name="position">1</property>
+            <property name="tab_fill">False</property>
+          </packing>
+        </child>
+        <child>
+          <placeholder/>
+        </child>
+        <child type="tab">
+          <placeholder/>
+        </child>
+      </object>
+    </child>
+  </object>
+</interface>
+
