siosm / rpms / gdm

Forked from rpms/gdm 2 years ago
Clone
Blob Blame History Raw
diff -up gdm-2.25.2/common/gdm-marshal.list.multistack-but-boring gdm-2.25.2/common/gdm-marshal.list
--- gdm-2.25.2/common/gdm-marshal.list.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/common/gdm-marshal.list	2009-03-04 21:03:53.097681845 -0500
@@ -5,3 +5,4 @@ VOID:STRING,STRING
 VOID:UINT,UINT
 VOID:STRING,INT
 VOID:DOUBLE
+BOOLEAN:STRING
diff -up gdm-2.25.2/configure.ac.multistack-but-boring gdm-2.25.2/configure.ac
--- gdm-2.25.2/configure.ac.multistack-but-boring	2009-03-04 21:03:53.075443680 -0500
+++ gdm-2.25.2/configure.ac	2009-03-04 21:03:53.099711915 -0500
@@ -18,6 +18,22 @@ AC_PROG_CXX
 AM_PROG_CC_C_O
 AC_PROG_LIBTOOL()
 
+## increment if the plugin interface has additions, changes, removals.
+LT_CURRENT=1
+
+## increment any time the source changes; set to
+##  0 if you increment CURRENT
+LT_REVISION=0
+
+## increment if any interfaces have been added; set to 0
+## if any interfaces have been changed or removed. removal has
+## precedence over adding, so set to 0 if both happened.
+LT_AGE=0
+
+AC_SUBST(LT_CURRENT)
+AC_SUBST(LT_REVISION)
+AC_SUBST(LT_AGE)
+
 AC_HEADER_STDC
 
 AC_SUBST(VERSION)
@@ -51,6 +67,7 @@ GNOME_PANEL_REQUIRED_VERSION=2.0.0
 LIBXKLAVIER_REQUIRED_VERSION=3.5
 #FONTCONFIG_REQUIRED_VERSION=2.6.0
 FONTCONFIG_REQUIRED_VERSION=2.5.0
+NSS_REQUIRED_VERSION=3.11.1
 
 EXTRA_COMPILE_WARNINGS(yes)
 
@@ -74,6 +91,12 @@ PKG_CHECK_MODULES(DAEMON,
 AC_SUBST(DAEMON_CFLAGS)
 AC_SUBST(DAEMON_LIBS)
 
+PKG_CHECK_MODULES(NSS,
+        nss >= $NSS_REQUIRED_VERSION
+)
+AC_SUBST(NSS_CFLAGS)
+AC_SUBST(NSS_LIBS)
+
 PKG_CHECK_MODULES(XLIB, x11 xau, ,
   [AC_PATH_XTRA
     if test "x$no_x" = xyes; then
@@ -200,6 +223,15 @@ AC_ARG_WITH(dmconfdir,
 AC_SUBST(dmconfdir)
 
 dnl ---------------------------------------------------------------------------
+dnl - Configuration file stuff
+dnl ---------------------------------------------------------------------------
+AC_ARG_WITH(extensionsdatadir,
+            AS_HELP_STRING([--with-extensions-datadir],
+                           [directory where extensions store data, default=DATADIR/gdm/simple-greeter/extensions]),
+            extensionsdatadir=${withval}, extensionsdatadir=${datadir}/gdm/simple-greeter/extensions)
+AC_SUBST(extensionsdatadir)
+
+dnl ---------------------------------------------------------------------------
 dnl - Configure arguments
 dnl ---------------------------------------------------------------------------
 
@@ -1292,6 +1324,22 @@ AC_SUBST(GDM_SPOOL_DIR)
 
 
 dnl ---------------------------------------------------------------------------
+dnl - Directory for simple greeter plugins
+dnl ---------------------------------------------------------------------------
+
+AC_ARG_WITH(simple-greeter-plugins-dir,
+            AS_HELP_STRING([--with-simple-greeter-plugins-dir=<dir>],
+                           [simple greeter plugins directory]))
+
+if ! test -z "$with_simple_greeter_plugins_dir"; then
+   GDM_SIMPLE_GREETER_PLUGINS_DIR=$with_simple_greeter_plugins_dir
+else
+   GDM_SIMPLE_GREETER_PLUGINS_DIR=${libdir}/gdm/simple-greeter/plugins
+fi
+
+AC_SUBST(GDM_SIMPLE_GREETER_PLUGINS_DIR)
+
+dnl ---------------------------------------------------------------------------
 dnl - Finish
 dnl ---------------------------------------------------------------------------
 
@@ -1420,6 +1468,12 @@ docs/Makefile
 gui/Makefile
 gui/simple-greeter/Makefile
 gui/simple-greeter/libnotificationarea/Makefile
+gui/simple-greeter/libgdmsimplegreeter/Makefile
+gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc
+gui/simple-greeter/plugins/Makefile
+gui/simple-greeter/plugins/password/Makefile
+gui/simple-greeter/plugins/fingerprint/Makefile
+gui/simple-greeter/plugins/smartcard/Makefile
 gui/simple-chooser/Makefile
 gui/user-switch-applet/Makefile
 utils/Makefile
diff -up gdm-2.25.2/daemon/gdm-factory-slave.c.multistack-but-boring gdm-2.25.2/daemon/gdm-factory-slave.c
--- gdm-2.25.2/daemon/gdm-factory-slave.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-factory-slave.c	2009-03-04 21:03:53.101710418 -0500
@@ -144,63 +144,71 @@ on_greeter_session_died (GdmGreeterSessi
 
 static void
 on_session_info (GdmSession      *session,
+                 const char      *service_name,
                  const char      *text,
                  GdmFactorySlave *slave)
 {
         g_debug ("GdmFactorySlave: Info: %s", text);
-        gdm_greeter_server_info (slave->priv->greeter_server, text);
+        gdm_greeter_server_info (slave->priv->greeter_server, service_name, text);
 }
 
 static void
 on_session_problem (GdmSession      *session,
+                    const char      *service_name,
                     const char      *text,
                     GdmFactorySlave *slave)
 {
         g_debug ("GdmFactorySlave: Problem: %s", text);
-        gdm_greeter_server_problem (slave->priv->greeter_server, text);
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, text);
 }
 
 static void
 on_session_info_query (GdmSession      *session,
+                       const char      *service_name,
                        const char      *text,
                        GdmFactorySlave *slave)
 {
 
         g_debug ("GdmFactorySlave: Info query: %s", text);
-        gdm_greeter_server_info_query (slave->priv->greeter_server, text);
+        gdm_greeter_server_info_query (slave->priv->greeter_server, service_name, text);
 }
 
 static void
 on_session_secret_info_query (GdmSession      *session,
+                              const char      *service_name,
                               const char      *text,
                               GdmFactorySlave *slave)
 {
         g_debug ("GdmFactorySlave: Secret info query: %s", text);
-        gdm_greeter_server_secret_info_query (slave->priv->greeter_server, text);
+        gdm_greeter_server_secret_info_query (slave->priv->greeter_server, service_name, text);
 }
 
 static void
-on_session_opened (GdmSession      *session,
-                   GdmFactorySlave *slave)
+on_session_conversation_started (GdmSession      *session,
+                                 const char      *service_name,
+                                 GdmFactorySlave *slave)
 {
-        g_debug ("GdmFactorySlave:  session opened");
+        g_debug ("GdmFactorySlave:  session conversation started");
 
-        gdm_greeter_server_ready (slave->priv->greeter_server);
+        gdm_greeter_server_ready (slave->priv->greeter_server,
+                                  service_name);
 }
 
 static void
 on_session_setup_complete (GdmSession      *session,
+                           const char      *service_name,
                            GdmFactorySlave *slave)
 {
-        gdm_session_authenticate (session);
+        gdm_session_authenticate (session, service_name);
 }
 
 static void
 on_session_setup_failed (GdmSession      *session,
+                         const char      *service_name,
                          const char      *message,
                          GdmFactorySlave *slave)
 {
-        gdm_greeter_server_problem (slave->priv->greeter_server, _("Unable to initialize login system"));
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to initialize login system"));
 
         queue_greeter_reset (slave);
 }
@@ -222,23 +230,26 @@ on_session_reset_failed (GdmSession     
 
 static void
 on_session_authenticated (GdmSession      *session,
+                          const char      *service_name,
                           GdmFactorySlave *slave)
 {
-        gdm_session_authorize (session);
+        gdm_session_authorize (session, service_name);
 }
 
 static void
 on_session_authentication_failed (GdmSession      *session,
+                                  const char      *service_name,
                                   const char      *message,
                                   GdmFactorySlave *slave)
 {
-        gdm_greeter_server_problem (slave->priv->greeter_server, _("Unable to authenticate user"));
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to authenticate user"));
 
         queue_greeter_reset (slave);
 }
 
 static void
 on_session_authorized (GdmSession      *session,
+                       const char      *service_name,
                        GdmFactorySlave *slave)
 {
         int flag;
@@ -246,39 +257,42 @@ on_session_authorized (GdmSession      *
         /* FIXME: check for migration? */
         flag = GDM_SESSION_CRED_ESTABLISH;
 
-        gdm_session_accredit (session, flag);
+        gdm_session_accredit (session, service_name, flag);
 }
 
 static void
 on_session_authorization_failed (GdmSession      *session,
+                                 const char      *service_name,
                                  const char      *message,
                                  GdmFactorySlave *slave)
 {
-        gdm_greeter_server_problem (slave->priv->greeter_server, _("Unable to authorize user"));
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to authorize user"));
 
         queue_greeter_reset (slave);
 }
 
 static void
 on_session_accredited (GdmSession      *session,
+                       const char      *service_name,
                        GdmFactorySlave *slave)
 {
         g_debug ("GdmFactorySlave:  session user verified");
 
-        gdm_session_start_session (session);
+        gdm_session_start_session (session, service_name);
 
         gdm_greeter_server_reset (slave->priv->greeter_server);
 }
 
 static void
 on_session_accreditation_failed (GdmSession      *session,
+                                 const char      *service_name,
                                  const char      *message,
                                  GdmFactorySlave *slave)
 {
         g_debug ("GdmFactorySlave: could not successfully authenticate user: %s",
                  message);
 
-        gdm_greeter_server_problem (slave->priv->greeter_server, _("Unable to establish credentials"));
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to establish credentials"));
 
         queue_greeter_reset (slave);
 }
@@ -366,37 +380,48 @@ on_session_relay_connected (GdmSessionRe
                             GdmFactorySlave *slave)
 {
         g_debug ("GdmFactorySlave: Relay Connected");
+}
+
+static void
+on_greeter_start_conversation (GdmGreeterServer *greeter_server,
+                               const char       *service_name,
+                               GdmFactorySlave  *slave)
+{
+        g_debug ("GdmFactorySlave: start conversation");
 
-        gdm_session_open (GDM_SESSION (slave->priv->session));
+        gdm_session_start_conversation (GDM_SESSION (slave->priv->session), service_name);
 }
 
 static void
 on_greeter_begin_verification (GdmGreeterServer *greeter_server,
+                               const char       *service_name,
                                GdmFactorySlave  *slave)
 {
         g_debug ("GdmFactorySlave: begin verification");
         gdm_session_setup (GDM_SESSION (slave->priv->session),
-                           "gdm");
+                           service_name);
 }
 
 static void
 on_greeter_begin_verification_for_user (GdmGreeterServer *greeter_server,
+                                        const char       *service_name,
                                         const char       *username,
                                         GdmFactorySlave  *slave)
 {
         g_debug ("GdmFactorySlave: begin verification for user");
         gdm_session_setup_for_user (GDM_SESSION (slave->priv->session),
-                                    "gdm",
+                                    service_name,
                                     username);
 }
 
 static void
 on_greeter_answer (GdmGreeterServer *greeter_server,
+                   const char       *service_name,
                    const char       *text,
                    GdmFactorySlave  *slave)
 {
         g_debug ("GdmFactorySlave: Greeter answer");
-        gdm_session_answer_query (GDM_SESSION (slave->priv->session), text);
+        gdm_session_answer_query (GDM_SESSION (slave->priv->session), service_name, text);
 }
 
 static void
@@ -493,6 +518,10 @@ run_greeter (GdmFactorySlave *slave)
 
         slave->priv->greeter_server = gdm_greeter_server_new (display_id);
         g_signal_connect (slave->priv->greeter_server,
+                          "start-conversation",
+                          G_CALLBACK (on_greeter_start_conversation),
+                          slave);
+        g_signal_connect (slave->priv->greeter_server,
                           "begin-verification",
                           G_CALLBACK (on_greeter_begin_verification),
                           slave);
@@ -694,8 +723,8 @@ gdm_factory_slave_start (GdmSlave *slave
 
         GDM_FACTORY_SLAVE (slave)->priv->session = gdm_session_relay_new ();
         g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session,
-                          "opened",
-                          G_CALLBACK (on_session_opened),
+                          "conversation-started",
+                          G_CALLBACK (on_session_conversation_started),
                           slave);
         g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session,
                           "setup-complete",
diff -up gdm-2.25.2/daemon/gdm-greeter-server.c.multistack-but-boring gdm-2.25.2/daemon/gdm-greeter-server.c
--- gdm-2.25.2/daemon/gdm-greeter-server.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-greeter-server.c	2009-03-04 21:03:53.104432962 -0500
@@ -43,6 +43,7 @@
 #include <dbus/dbus-glib.h>
 #include <dbus/dbus-glib-lowlevel.h>
 
+#include "gdm-marshal.h"
 #include "gdm-greeter-server.h"
 
 #define GDM_GREETER_SERVER_DBUS_PATH      "/org/gnome/DisplayManager/GreeterServer"
@@ -69,6 +70,7 @@ enum {
 };
 
 enum {
+        START_CONVERSATION,
         BEGIN_AUTO_LOGIN,
         BEGIN_VERIFICATION,
         BEGIN_VERIFICATION_FOR_USER,
@@ -155,6 +157,46 @@ send_dbus_string_and_int_signal (GdmGree
 }
 
 static void
+send_dbus_string_string_signal (GdmGreeterServer *greeter_server,
+                                const char       *name,
+                                const char       *text1,
+                                const char       *text2)
+{
+        DBusMessage    *message;
+        DBusMessageIter iter;
+        const char     *str;
+
+        g_return_if_fail (greeter_server != NULL);
+
+        message = dbus_message_new_signal (GDM_GREETER_SERVER_DBUS_PATH,
+                                           GDM_GREETER_SERVER_DBUS_INTERFACE,
+                                           name);
+
+        dbus_message_iter_init_append (message, &iter);
+
+        if (text1 != NULL) {
+                str = text1;
+        } else {
+                str = "";
+        }
+        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &str);
+
+        if (text2 != NULL) {
+                str = text2;
+        } else {
+                str = "";
+        }
+        dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &str);
+
+        g_debug ("GreeterServer: Sending %s (%s)", name, str);
+        if (! send_dbus_message (greeter_server->priv->greeter_connection, message)) {
+                g_debug ("GreeterServer: Could not send %s signal", name);
+        }
+
+        dbus_message_unref (message);
+}
+
+static void
 send_dbus_string_signal (GdmGreeterServer *greeter_server,
                          const char       *name,
                          const char       *text)
@@ -207,34 +249,38 @@ send_dbus_void_signal (GdmGreeterServer 
 
 gboolean
 gdm_greeter_server_info_query (GdmGreeterServer *greeter_server,
+                               const char       *service_name,
                                const char       *text)
 {
-        send_dbus_string_signal (greeter_server, "InfoQuery", text);
+        send_dbus_string_string_signal (greeter_server, "InfoQuery", service_name, text);
 
         return TRUE;
 }
 
 gboolean
 gdm_greeter_server_secret_info_query (GdmGreeterServer *greeter_server,
+                                      const char       *service_name,
                                       const char       *text)
 {
-        send_dbus_string_signal (greeter_server, "SecretInfoQuery", text);
+        send_dbus_string_string_signal (greeter_server, "SecretInfoQuery", service_name, text);
         return TRUE;
 }
 
 gboolean
 gdm_greeter_server_info (GdmGreeterServer *greeter_server,
+                         const char       *service_name,
                          const char       *text)
 {
-        send_dbus_string_signal (greeter_server, "Info", text);
+        send_dbus_string_string_signal (greeter_server, "Info", service_name, text);
         return TRUE;
 }
 
 gboolean
 gdm_greeter_server_problem (GdmGreeterServer *greeter_server,
+                            const char       *service_name,
                             const char       *text)
 {
-        send_dbus_string_signal (greeter_server, "Problem", text);
+        send_dbus_string_string_signal (greeter_server, "Problem", service_name, text);
         return TRUE;
 }
 
@@ -246,9 +292,18 @@ gdm_greeter_server_reset (GdmGreeterServ
 }
 
 gboolean
-gdm_greeter_server_ready (GdmGreeterServer *greeter_server)
+gdm_greeter_server_ready (GdmGreeterServer *greeter_server,
+                          const char       *service_name)
 {
-        send_dbus_void_signal (greeter_server, "Ready");
+        send_dbus_string_signal (greeter_server, "Ready", service_name);
+        return TRUE;
+}
+
+gboolean
+gdm_greeter_server_conversation_stopped (GdmGreeterServer *greeter_server,
+                                         const char       *service_name)
+{
+        send_dbus_string_signal (greeter_server, "ConversationStopped", service_name);
         return TRUE;
 }
 
@@ -289,9 +344,10 @@ gdm_greeter_server_request_timed_login (
 }
 
 void
-gdm_greeter_server_user_authorized (GdmGreeterServer *greeter_server)
+gdm_greeter_server_user_authorized (GdmGreeterServer *greeter_server,
+                                    const char       *service_name)
 {
-        send_dbus_void_signal (greeter_server, "UserAuthorized");
+        send_dbus_string_signal (greeter_server, "UserAuthorized", service_name);
 }
 
 /* Note: Use abstract sockets like dbus does by default on Linux. Abstract
@@ -323,11 +379,49 @@ generate_address (void)
 }
 
 static DBusHandlerResult
+handle_start_conversation (GdmGreeterServer *greeter_server,
+                           DBusConnection   *connection,
+                           DBusMessage      *message)
+{
+        DBusMessage *reply;
+        DBusError    error;
+        const char  *service_name;
+
+        dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
+
+        g_debug ("GreeterServer: StartConversation");
+
+        reply = dbus_message_new_method_return (message);
+        dbus_connection_send (connection, reply, NULL);
+        dbus_message_unref (reply);
+
+        g_signal_emit (greeter_server, signals [START_CONVERSATION], 0, service_name);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
 handle_begin_verification (GdmGreeterServer *greeter_server,
                            DBusConnection   *connection,
                            DBusMessage      *message)
 {
         DBusMessage *reply;
+        DBusError    error;
+        const char  *service_name;
+
+        dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GreeterServer: BeginVerification");
 
@@ -335,7 +429,7 @@ handle_begin_verification (GdmGreeterSer
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        g_signal_emit (greeter_server, signals [BEGIN_VERIFICATION], 0);
+        g_signal_emit (greeter_server, signals [BEGIN_VERIFICATION], 0, service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -349,7 +443,6 @@ handle_begin_auto_login (GdmGreeterServe
         DBusError    error;
         const char  *text;
 
-
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
                                      DBUS_TYPE_STRING, &text,
@@ -376,13 +469,16 @@ handle_begin_verification_for_user (GdmG
         DBusMessage *reply;
         DBusError    error;
         const char  *text;
+        const char  *service_name;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_STRING, &text,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
         }
+        dbus_error_free (&error);
 
         g_debug ("GreeterServer: BeginVerificationForUser for '%s'", text);
 
@@ -390,7 +486,7 @@ handle_begin_verification_for_user (GdmG
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        g_signal_emit (greeter_server, signals [BEGIN_VERIFICATION_FOR_USER], 0, text);
+        g_signal_emit (greeter_server, signals [BEGIN_VERIFICATION_FOR_USER], 0, service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -403,13 +499,16 @@ handle_answer_query (GdmGreeterServer *g
         DBusMessage *reply;
         DBusError    error;
         const char  *text;
+        const char  *service_name;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_STRING, &text,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
         }
+        dbus_error_free (&error);
 
         g_debug ("GreeterServer: AnswerQuery");
 
@@ -417,7 +516,7 @@ handle_answer_query (GdmGreeterServer *g
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        g_signal_emit (greeter_server, signals [QUERY_ANSWER], 0, text);
+        g_signal_emit (greeter_server, signals [QUERY_ANSWER], 0, service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -614,9 +713,11 @@ handle_start_session_when_ready (GdmGree
         DBusMessage *reply;
         DBusError    error;
         gboolean     should_start_session;
+        char        *service_name;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_BOOLEAN, &should_start_session,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
@@ -630,9 +731,9 @@ handle_start_session_when_ready (GdmGree
         dbus_message_unref (reply);
 
         if (should_start_session) {
-                g_signal_emit (greeter_server, signals [START_SESSION_WHEN_READY], 0);
+                g_signal_emit (greeter_server, signals [START_SESSION_WHEN_READY], 0, service_name);
         } else {
-                g_signal_emit (greeter_server, signals [START_SESSION_LATER] ,0);
+                g_signal_emit (greeter_server, signals [START_SESSION_LATER] ,0, service_name);
         }
 
         return DBUS_HANDLER_RESULT_HANDLED;
@@ -645,7 +746,9 @@ greeter_handle_child_message (DBusConnec
 {
         GdmGreeterServer *greeter_server = GDM_GREETER_SERVER (user_data);
 
-        if (dbus_message_is_method_call (message, GDM_GREETER_SERVER_DBUS_INTERFACE, "BeginVerification")) {
+        if (dbus_message_is_method_call (message, GDM_GREETER_SERVER_DBUS_INTERFACE, "StartConversation")) {
+                return handle_start_conversation (greeter_server, connection, message);
+        } else if (dbus_message_is_method_call (message, GDM_GREETER_SERVER_DBUS_INTERFACE, "BeginVerification")) {
                 return handle_begin_verification (greeter_server, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_GREETER_SERVER_DBUS_INTERFACE, "BeginVerificationForUser")) {
                 return handle_begin_verification_for_user (greeter_server, connection, message);
@@ -699,13 +802,23 @@ do_introspect (DBusConnection *connectio
         /* interface */
         xml = g_string_append (xml,
                                "  <interface name=\"org.gnome.DisplayManager.GreeterServer\">\n"
+                               "    <method name=\"StartConversation\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
+                               "    </method>\n"
+                               "    <method name=\"StopConversation\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
+                               "    </method>\n"
                                "    <method name=\"BeginVerification\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
+                               "    </method>\n"
                                "    <method name=\"BeginTimedLogin\">\n"
                                "    </method>\n"
                                "    <method name=\"BeginVerificationForUser\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"username\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"AnswerQuery\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"SelectSession\">\n"
@@ -728,18 +841,23 @@ do_introspect (DBusConnection *connectio
                                "      <arg name=\"id\" direction=\"out\" type=\"o\"/>\n"
                                "    </method>\n"
                                "    <method name=\"StartSessionWhenReady\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"should_start_session\" type=\"b\"/>\n"
                                "    </method>\n"
                                "    <signal name=\"Info\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Problem\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"InfoQuery\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"SecretInfoQuery\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "      <arg name=\"text\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"SelectedUserChanged\">\n"
@@ -752,7 +870,6 @@ do_introspect (DBusConnection *connectio
                                "      <arg name=\"layout_name\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"DefaultSessionNameChanged\">\n"
-                               "    <signal name=\"DefaultSessionNameChanged\">\n"
                                "      <arg name=\"session_name\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"TimedLoginRequested\">\n"
@@ -760,10 +877,15 @@ do_introspect (DBusConnection *connectio
                                "      <arg name=\"delay\" type=\"i\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Ready\">\n"
+                               "      <arg name=\"service-name\" type=\"s\"/>\n"
+                               "    </signal>\n"
+                               "    <signal name=\"ConversationStopped\">\n"
+                               "      <arg name=\"service-name\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Reset\">\n"
                                "    </signal>\n"
                                "    <signal name=\"UserAuthorized\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "  </interface>\n");
 
@@ -1122,6 +1244,16 @@ gdm_greeter_server_class_init (GdmGreete
                                                               "group name",
                                                               GDM_GROUPNAME,
                                                               G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+        signals [START_CONVERSATION] =
+                g_signal_new ("start-conversation",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterServerClass, start_conversation),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1, G_TYPE_STRING);
         signals [BEGIN_VERIFICATION] =
                 g_signal_new ("begin-verification",
                               G_OBJECT_CLASS_TYPE (object_class),
@@ -1129,9 +1261,9 @@ gdm_greeter_server_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterServerClass, begin_verification),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
         signals [BEGIN_AUTO_LOGIN] =
                 g_signal_new ("begin-auto-login",
                               G_OBJECT_CLASS_TYPE (object_class),
@@ -1150,10 +1282,10 @@ gdm_greeter_server_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterServerClass, begin_verification_for_user),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [QUERY_ANSWER] =
                 g_signal_new ("query-answer",
                               G_OBJECT_CLASS_TYPE (object_class),
@@ -1161,10 +1293,10 @@ gdm_greeter_server_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterServerClass, query_answer),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [SESSION_SELECTED] =
                 g_signal_new ("session-selected",
                               G_OBJECT_CLASS_TYPE (object_class),
@@ -1258,9 +1390,9 @@ gdm_greeter_server_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterServerClass, start_session_when_ready),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
 
         signals [START_SESSION_LATER] =
                 g_signal_new ("start-session-later",
@@ -1269,9 +1401,9 @@ gdm_greeter_server_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterServerClass, start_session_later),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
 }
 
 static void
diff -up gdm-2.25.2/daemon/gdm-greeter-server.h.multistack-but-boring gdm-2.25.2/daemon/gdm-greeter-server.h
--- gdm-2.25.2/daemon/gdm-greeter-server.h.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-greeter-server.h	2009-03-04 21:03:53.105432039 -0500
@@ -45,11 +45,16 @@ typedef struct
 {
         GObjectClass   parent_class;
 
+        void (* start_conversation)         (GdmGreeterServer  *greeter_server,
+                                             const char        *service_name);
         void (* begin_auto_login)           (GdmGreeterServer  *greeter_server);
-        void (* begin_verification)         (GdmGreeterServer  *greeter_server);
+        void (* begin_verification)         (GdmGreeterServer  *greeter_server,
+                                             const char        *service_name);
         void (* begin_verification_for_user)(GdmGreeterServer  *greeter_server,
+                                             const char        *service_name,
                                              const char        *username);
         void (* query_answer)               (GdmGreeterServer  *greeter_server,
+                                             const char        *service_name,
                                              const char        *text);
         void (* session_selected)           (GdmGreeterServer  *greeter_server,
                                              const char        *name);
@@ -64,7 +69,8 @@ typedef struct
         void (* cancelled)                  (GdmGreeterServer  *greeter_server);
         void (* connected)                  (GdmGreeterServer  *greeter_server);
         void (* disconnected)               (GdmGreeterServer  *greeter_server);
-        void (* start_session_when_ready)   (GdmGreeterServer  *greeter_server);
+        void (* start_session_when_ready)   (GdmGreeterServer  *greeter_server,
+                                             const char        *service_name);
         void (* start_session_later)        (GdmGreeterServer  *greeter_server);
 } GdmGreeterServerClass;
 
@@ -75,17 +81,23 @@ gboolean            gdm_greeter_server_s
 gboolean            gdm_greeter_server_stop                  (GdmGreeterServer *greeter_server);
 char *              gdm_greeter_server_get_address           (GdmGreeterServer *greeter_server);
 
-
 gboolean            gdm_greeter_server_info_query            (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name,
                                                               const char       *text);
 gboolean            gdm_greeter_server_secret_info_query     (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name,
                                                               const char       *text);
 gboolean            gdm_greeter_server_info                  (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name,
                                                               const char       *text);
 gboolean            gdm_greeter_server_problem               (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name,
                                                               const char       *text);
 gboolean            gdm_greeter_server_reset                 (GdmGreeterServer *greeter_server);
-gboolean            gdm_greeter_server_ready                 (GdmGreeterServer *greeter_server);
+gboolean            gdm_greeter_server_ready                 (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name);
+gboolean            gdm_greeter_server_conversation_stopped  (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name);
 void                gdm_greeter_server_selected_user_changed (GdmGreeterServer *greeter_server,
                                                               const char       *text);
 void                gdm_greeter_server_default_language_name_changed (GdmGreeterServer *greeter_server,
@@ -98,8 +110,8 @@ void                gdm_greeter_server_d
 void                gdm_greeter_server_request_timed_login   (GdmGreeterServer *greeter_server,
                                                               const char       *username,
                                                               int               delay);
-void                gdm_greeter_server_user_authorized       (GdmGreeterServer *greeter_server);
-
+void                gdm_greeter_server_user_authorized       (GdmGreeterServer *greeter_server,
+                                                              const char       *service_name);
 
 G_END_DECLS
 
diff -up gdm-2.25.2/daemon/gdm-product-slave.c.multistack-but-boring gdm-2.25.2/daemon/gdm-product-slave.c
--- gdm-2.25.2/daemon/gdm-product-slave.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-product-slave.c	2009-03-04 21:03:53.107432008 -0500
@@ -79,6 +79,8 @@ struct GdmProductSlavePrivate
 
         DBusGProxy       *product_display_proxy;
         DBusGConnection  *connection;
+
+        char             *start_session_service_name;
 };
 
 enum {
@@ -93,6 +95,68 @@ static void     gdm_product_slave_finali
 G_DEFINE_TYPE (GdmProductSlave, gdm_product_slave, GDM_TYPE_SLAVE)
 
 static gboolean
+send_dbus_string_string_method (DBusConnection *connection,
+                                const char     *method,
+                                const char     *payload1,
+                                const char     *payload2)
+{
+        DBusError       error;
+        DBusMessage    *message;
+        DBusMessage    *reply;
+        DBusMessageIter iter;
+        const char     *str;
+
+        g_debug ("GdmProductSlave: Calling %s", method);
+        message = dbus_message_new_method_call (NULL,
+                                                RELAY_SERVER_DBUS_PATH,
+                                                RELAY_SERVER_DBUS_INTERFACE,
+                                                method);
+        if (message == NULL) {
+                g_warning ("Couldn't allocate the D-Bus message");
+                return FALSE;
+        }
+
+        dbus_message_iter_init_append (message, &iter);
+
+        if (payload1 != NULL) {
+                str = payload1;
+        } else {
+                str = "";
+        }
+        dbus_message_iter_append_basic (&iter,
+                                        DBUS_TYPE_STRING,
+                                        &str);
+        if (payload2 != NULL) {
+                str = payload2;
+        } else {
+                str = "";
+        }
+        dbus_message_iter_append_basic (&iter,
+                                        DBUS_TYPE_STRING,
+                                        &str);
+        dbus_error_init (&error);
+        reply = dbus_connection_send_with_reply_and_block (connection,
+                                                           message,
+                                                           -1,
+                                                           &error);
+
+        dbus_message_unref (message);
+
+        if (dbus_error_is_set (&error)) {
+                g_warning ("%s %s raised: %s\n",
+                           method,
+                           error.name,
+                           error.message);
+                return FALSE;
+        }
+        if (reply != NULL) {
+                dbus_message_unref (reply);
+        }
+        dbus_connection_flush (connection);
+
+        return TRUE;
+}
+static gboolean
 send_dbus_string_method (DBusConnection *connection,
                          const char     *method,
                          const char     *payload)
@@ -246,19 +310,21 @@ relay_session_started (GdmProductSlave *
 }
 
 static void
-relay_session_opened (GdmProductSlave *slave)
+relay_session_conversation_started (GdmProductSlave *slave,
+                                    const char      *service_name)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "Opened");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "ConversationStarted", service_name);
 }
 
 static void
-on_session_opened (GdmSession      *session,
-                   GdmProductSlave *slave)
+on_session_conversation_started (GdmSession      *session,
+                                 const char      *service_name,
+                                 GdmProductSlave *slave)
 {
-        g_debug ("GdmProductSlave: session opened");
+        g_debug ("GdmProductSlave: session conversation started");
 
-        relay_session_opened (slave);
+        relay_session_conversation_started (slave, service_name);
 }
 
 static void
@@ -354,7 +420,8 @@ setup_session (GdmProductSlave *slave)
         g_free (display_device);
         g_free (auth_file);
 
-        gdm_session_start_session (GDM_SESSION (slave->priv->session));
+        gdm_session_start_session (GDM_SESSION (slave->priv->session),
+                                   slave->priv->start_session_service_name);
 
         return TRUE;
 }
@@ -506,96 +573,112 @@ on_session_reset_failed (GdmSession     
 
 static void
 on_session_authenticated (GdmSession      *session,
+                          const char      *service_name,
                           GdmProductSlave *slave)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "Authenticated");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "Authenticated", service_name);
 }
 
 static void
 on_session_authentication_failed (GdmSession      *session,
+                                  const char      *service_name,
                                   const char      *message,
                                   GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "AuthenticationFailed",
-                                 message);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "AuthenticationFailed",
+                                        service_name,
+                                        message);
 }
 
 static void
 on_session_authorized (GdmSession      *session,
+                       const char      *service_name,
                        GdmProductSlave *slave)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "Authorized");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "Authorized", service_name);
 }
 
 static void
 on_session_authorization_failed (GdmSession      *session,
+                                 const char      *service_name,
                                  const char      *message,
                                  GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "AuthorizationFailed",
-                                 message);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "AuthorizationFailed",
+                                        service_name,
+                                        message);
 }
 
 static void
 on_session_accredited (GdmSession      *session,
+                       const char      *service_name,
                        GdmProductSlave *slave)
 {
-        send_dbus_void_method (slave->priv->session_relay_connection,
-                               "Accredited");
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "Accredited", service_name);
 }
 
 static void
 on_session_accreditation_failed (GdmSession      *session,
+                                 const char      *service_name,
                                  const char      *message,
                                  GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "AccreditationFailed",
-                                 message);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "AccreditationFailed",
+                                        service_name,
+                                        message);
 }
 
 static void
 on_session_info (GdmSession      *session,
+                 const char      *service_name,
                  const char      *text,
                  GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "Info",
-                                 text);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "Info",
+                                        service_name,
+                                        text);
 }
 
 static void
 on_session_problem (GdmSession      *session,
+                    const char      *service_name,
                     const char      *text,
                     GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "Problem",
-                                 text);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "Problem",
+                                        service_name,
+                                        text);
 }
 
 static void
 on_session_info_query (GdmSession      *session,
+                       const char      *service_name,
                        const char      *text,
                        GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "InfoQuery",
-                                 text);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "InfoQuery",
+                                        service_name, text);
 }
 
 static void
 on_session_secret_info_query (GdmSession      *session,
+                              const char      *service_name,
                               const char      *text,
                               GdmProductSlave *slave)
 {
-        send_dbus_string_method (slave->priv->session_relay_connection,
-                                 "SecretInfoQuery",
-                                 text);
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "SecretInfoQuery",
+                                        service_name,
+                                        text);
 }
 
 static void
@@ -656,36 +739,92 @@ static void
 on_relay_authenticate (GdmProductSlave *slave,
                        DBusMessage     *message)
 {
-        g_debug ("GdmProductSlave: Relay Authenticate");
+        DBusError   error;
+        char *service_name;
+        dbus_bool_t res;
 
-        gdm_session_authenticate (GDM_SESSION (slave->priv->session));
+        dbus_error_init (&error);
+        res = dbus_message_get_args (message,
+                                     &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID);
+        if (res) {
+                g_debug ("GdmProductSlave: Relay Authenticate");
+                gdm_session_authenticate (GDM_SESSION (slave->priv->session), service_name);
+        } else {
+                g_warning ("Unable to get arguments: %s", error.message);
+                dbus_error_free (&error);
+        }
+        dbus_error_free (&error);
 }
 
 static void
 on_relay_authorize (GdmProductSlave *slave,
                     DBusMessage     *message)
 {
-        g_debug ("GdmProductSlave: Relay Authorize");
+        DBusError   error;
+        char *service_name;
+        dbus_bool_t res;
 
-        gdm_session_authorize (GDM_SESSION (slave->priv->session));
+        dbus_error_init (&error);
+        res = dbus_message_get_args (message,
+                                     &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID);
+        if (res) {
+                g_debug ("GdmProductSlave: Relay Authorize");
+                gdm_session_authorize (GDM_SESSION (slave->priv->session), service_name);
+        } else {
+                g_warning ("Unable to get arguments: %s", error.message);
+                dbus_error_free (&error);
+        }
+        dbus_error_free (&error);
 }
 
 static void
 on_relay_establish_credentials (GdmProductSlave *slave,
                                 DBusMessage     *message)
 {
-        g_debug ("GdmProductSlave: Relay EstablishCredentials");
+        DBusError   error;
+        char *service_name;
+        dbus_bool_t res;
 
-        gdm_session_accredit (GDM_SESSION (slave->priv->session), GDM_SESSION_CRED_ESTABLISH);
+        dbus_error_init (&error);
+        res = dbus_message_get_args (message,
+                                     &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID);
+        if (res) {
+                g_debug ("GdmProductSlave: Relay EstablishCredentials");
+                gdm_session_accredit (GDM_SESSION (slave->priv->session), service_name, GDM_SESSION_CRED_ESTABLISH);
+        } else {
+                g_warning ("Unable to get arguments: %s", error.message);
+                dbus_error_free (&error);
+        }
+        dbus_error_free (&error);
 }
 
 static void
 on_relay_refresh_credentials (GdmProductSlave *slave,
                               DBusMessage     *message)
 {
-        g_debug ("GdmProductSlave: Relay RefreshCredentials");
+        DBusError   error;
+        char *service_name;
+        dbus_bool_t res;
 
-        gdm_session_accredit (GDM_SESSION (slave->priv->session), GDM_SESSION_CRED_REFRESH);
+        dbus_error_init (&error);
+        res = dbus_message_get_args (message,
+                                     &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID);
+        if (res) {
+                g_debug ("GdmProductSlave: Relay RefreshCredentials");
+                gdm_session_accredit (GDM_SESSION (slave->priv->session), service_name, GDM_SESSION_CRED_REFRESH);
+        } else {
+                g_warning ("Unable to get arguments: %s", error.message);
+                dbus_error_free (&error);
+        }
+        dbus_error_free (&error);
 }
 
 static void
@@ -694,16 +833,18 @@ on_relay_answer_query (GdmProductSlave *
 {
         DBusError   error;
         const char *text;
+        const char *service_name;
         dbus_bool_t res;
 
         dbus_error_init (&error);
         res = dbus_message_get_args (message,
                                      &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_STRING, &text,
                                      DBUS_TYPE_INVALID);
         if (res) {
                 g_debug ("GdmProductSlave: Relay AnswerQuery");
-                gdm_session_answer_query (GDM_SESSION (slave->priv->session), text);
+                gdm_session_answer_query (GDM_SESSION (slave->priv->session), service_name, text);
         } else {
                 g_warning ("Unable to get arguments: %s", error.message);
                 dbus_error_free (&error);
@@ -784,17 +925,52 @@ on_relay_user_selected (GdmProductSlave 
 }
 
 static void
-on_relay_open (GdmProductSlave *slave,
-               DBusMessage     *message)
+on_relay_start_conversation (GdmProductSlave *slave,
+                             DBusMessage     *message)
 {
-        gdm_session_open (GDM_SESSION (slave->priv->session));
+        DBusError   error;
+        char *service_name;
+        dbus_bool_t res;
+
+        dbus_error_init (&error);
+        res = dbus_message_get_args (message,
+                                     &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID);
+        if (res) {
+                g_debug ("GdmProductSlave: Started conversation with %s service", service_name);
+                gdm_session_start_conversation (GDM_SESSION (slave->priv->session),
+                                                service_name);
+        } else {
+                g_warning ("Unable to get arguments: %s", error.message);
+        }
+
+        dbus_error_free (&error);
 }
 
 static void
 on_relay_start_session (GdmProductSlave *slave,
                         DBusMessage     *message)
 {
-        gdm_product_slave_create_server (slave);
+        DBusError   error;
+        const char *service_name;
+        dbus_bool_t res;
+
+        dbus_error_init (&error);
+
+        res = dbus_message_get_args (message,
+                                     &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID);
+        if (res) {
+                g_debug ("GdmProductSlave: Relay StartSession");
+                g_free (slave->priv->start_session_service_name);
+                slave->priv->start_session_service_name = g_strdup (service_name);
+                gdm_product_slave_create_server (slave);
+        } else {
+                g_warning ("Unable to get arguments: %s", error.message);
+                dbus_error_free (&error);
+        }
 }
 
 static void
@@ -832,8 +1008,8 @@ create_new_session (GdmProductSlave *sla
         g_free (display_device);
 
         g_signal_connect (slave->priv->session,
-                          "opened",
-                          G_CALLBACK (on_session_opened),
+                          "conversation-started",
+                          G_CALLBACK (on_session_conversation_started),
                           slave);
         g_signal_connect (slave->priv->session,
                           "setup-complete",
@@ -991,8 +1167,8 @@ relay_dbus_handle_message (DBusConnectio
                 on_relay_user_selected (slave, message);
         } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "StartSession")) {
                 on_relay_start_session (slave, message);
-        } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "Open")) {
-                on_relay_open (slave, message);
+        } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "StartConversation")) {
+                on_relay_start_conversation (slave, message);
         } else if (dbus_message_is_signal (message, RELAY_SERVER_DBUS_INTERFACE, "Cancelled")) {
                 on_relay_cancelled (slave, message);
         } else {
diff -up gdm-2.25.2/daemon/gdm-session.c.multistack-but-boring gdm-2.25.2/daemon/gdm-session.c
--- gdm-2.25.2/daemon/gdm-session.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-session.c	2009-03-04 21:03:53.110432729 -0500
@@ -24,11 +24,13 @@
 #include <glib/gi18n.h>
 #include <glib-object.h>
 
+#include "gdm-marshal.h"
 #include "gdm-session.h"
 #include "gdm-session-private.h"
 
 enum {
-        OPENED = 0,
+        CONVERSATION_STARTED = 0,
+        CONVERSATION_STOPPED,
         SETUP_COMPLETE,
         SETUP_FAILED,
         RESET_COMPLETE,
@@ -78,11 +80,21 @@ gdm_session_get_type (void)
 }
 
 void
-gdm_session_open (GdmSession *session)
+gdm_session_start_conversation (GdmSession *session,
+                                const char *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        GDM_SESSION_GET_IFACE (session)->open (session);
+        GDM_SESSION_GET_IFACE (session)->start_conversation (session, service_name);
+}
+
+void
+gdm_session_stop_conversation (GdmSession *session,
+                              const char *service_name)
+{
+        g_return_if_fail (GDM_IS_SESSION (session));
+
+        GDM_SESSION_GET_IFACE (session)->stop_conversation (session, service_name);
 }
 
 void
@@ -113,37 +125,41 @@ gdm_session_setup_for_user (GdmSession *
 }
 
 void
-gdm_session_authenticate (GdmSession *session)
+gdm_session_authenticate (GdmSession *session,
+                          const char *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        GDM_SESSION_GET_IFACE (session)->authenticate (session);
+        GDM_SESSION_GET_IFACE (session)->authenticate (session, service_name);
 }
 
 void
-gdm_session_authorize (GdmSession *session)
+gdm_session_authorize (GdmSession *session,
+                       const char *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        GDM_SESSION_GET_IFACE (session)->authorize (session);
+        GDM_SESSION_GET_IFACE (session)->authorize (session, service_name);
 }
 
 void
 gdm_session_accredit (GdmSession *session,
+                      const char *service_name,
                       int         flag)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        GDM_SESSION_GET_IFACE (session)->accredit (session, flag);
+        GDM_SESSION_GET_IFACE (session)->accredit (session, service_name, flag);
 }
 
 void
 gdm_session_answer_query (GdmSession *session,
+                          const char *service_name,
                           const char *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        GDM_SESSION_GET_IFACE (session)->answer_query (session, text);
+        GDM_SESSION_GET_IFACE (session)->answer_query (session, service_name, text);
 }
 
 void
@@ -191,11 +207,12 @@ gdm_session_cancel (GdmSession *session)
 }
 
 void
-gdm_session_start_session (GdmSession *session)
+gdm_session_start_session (GdmSession *session,
+                           const char *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        GDM_SESSION_GET_IFACE (session)->start_session (session);
+        GDM_SESSION_GET_IFACE (session)->start_session (session, service_name);
 }
 
 static void
@@ -203,16 +220,26 @@ gdm_session_class_init (gpointer g_iface
 {
         GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
 
-        signals [OPENED] =
-                g_signal_new ("opened",
+        signals [CONVERSATION_STARTED] =
+                g_signal_new ("conversation-started",
                               iface_type,
                               G_SIGNAL_RUN_FIRST,
-                              G_STRUCT_OFFSET (GdmSessionIface, opened),
+                              G_STRUCT_OFFSET (GdmSessionIface, conversation_started),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
+        signals [CONVERSATION_STOPPED] =
+                g_signal_new ("conversation-stopped",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSessionIface, conversation_stopped),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1, G_TYPE_STRING);
         signals [SETUP_COMPLETE] =
                 g_signal_new ("setup-complete",
                               iface_type,
@@ -220,9 +247,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, setup_complete),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1,
+                              G_TYPE_STRING);
         signals [SETUP_FAILED] =
                 g_signal_new ("setup-failed",
                               iface_type,
@@ -230,10 +258,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, setup_failed),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [RESET_COMPLETE] =
                 g_signal_new ("reset-complete",
                               iface_type,
@@ -262,9 +290,9 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, authenticated),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
         signals [AUTHENTICATION_FAILED] =
                 g_signal_new ("authentication-failed",
                               iface_type,
@@ -272,10 +300,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, authentication_failed),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [AUTHORIZED] =
                 g_signal_new ("authorized",
                               iface_type,
@@ -283,9 +311,9 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, authorized),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
         signals [AUTHORIZATION_FAILED] =
                 g_signal_new ("authorization-failed",
                               iface_type,
@@ -293,10 +321,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, authorization_failed),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [ACCREDITED] =
                 g_signal_new ("accredited",
                               iface_type,
@@ -304,9 +332,9 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, accredited),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
         signals [ACCREDITATION_FAILED] =
                 g_signal_new ("accreditation-failed",
                               iface_type,
@@ -314,10 +342,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, accreditation_failed),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
 
          signals [INFO_QUERY] =
                 g_signal_new ("info-query",
@@ -326,10 +354,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, info_query),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [SECRET_INFO_QUERY] =
                 g_signal_new ("secret-info-query",
                               iface_type,
@@ -337,10 +365,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, secret_info_query),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [INFO] =
                 g_signal_new ("info",
                               iface_type,
@@ -348,10 +376,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, info),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [PROBLEM] =
                 g_signal_new ("problem",
                               iface_type,
@@ -359,10 +387,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, problem),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [SESSION_STARTED] =
                 g_signal_new ("session-started",
                               iface_type,
@@ -370,10 +398,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, session_started),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__INT,
+                              gdm_marshal_VOID__STRING_INT,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_INT);
+                              2,
+                              G_TYPE_STRING, G_TYPE_INT);
         signals [SESSION_START_FAILED] =
                 g_signal_new ("session-start-failed",
                               iface_type,
@@ -381,10 +409,10 @@ gdm_session_class_init (gpointer g_iface
                               G_STRUCT_OFFSET (GdmSessionIface, session_start_failed),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [SESSION_EXITED] =
                 g_signal_new ("session-exited",
                               iface_type,
@@ -464,19 +492,21 @@ gdm_session_class_init (gpointer g_iface
 }
 
 void
-_gdm_session_setup_complete (GdmSession   *session)
+_gdm_session_setup_complete (GdmSession   *session,
+                             const char   *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        g_signal_emit (session, signals [SETUP_COMPLETE], 0);
+        g_signal_emit (session, signals [SETUP_COMPLETE], 0, service_name);
 }
 
 void
 _gdm_session_setup_failed (GdmSession   *session,
+                           const char   *service_name,
                            const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [SETUP_FAILED], 0, text);
+        g_signal_emit (session, signals [SETUP_FAILED], 0, service_name, text);
 }
 
 void
@@ -496,99 +526,111 @@ _gdm_session_reset_failed (GdmSession   
 }
 
 void
-_gdm_session_authenticated (GdmSession   *session)
+_gdm_session_authenticated (GdmSession   *session,
+                            const char   *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        g_signal_emit (session, signals [AUTHENTICATED], 0);
+        g_signal_emit (session, signals [AUTHENTICATED], 0, service_name);
 }
 
 void
 _gdm_session_authentication_failed (GdmSession   *session,
+                                    const char   *service_name,
                                     const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [AUTHENTICATION_FAILED], 0, text);
+        g_signal_emit (session, signals [AUTHENTICATION_FAILED], 0, service_name, text);
 }
 
 void
-_gdm_session_authorized (GdmSession   *session)
+_gdm_session_authorized (GdmSession   *session,
+                         const char   *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        g_signal_emit (session, signals [AUTHORIZED], 0);
+        g_signal_emit (session, signals [AUTHORIZED], 0, service_name);
 }
 
 void
 _gdm_session_authorization_failed (GdmSession   *session,
+                                   const char   *service_name,
                                    const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [AUTHORIZATION_FAILED], 0, text);
+        g_signal_emit (session, signals [AUTHORIZATION_FAILED], 0, service_name, text);
 }
 
 void
-_gdm_session_accredited (GdmSession   *session)
+_gdm_session_accredited (GdmSession   *session,
+                         const char   *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
 
-        g_signal_emit (session, signals [ACCREDITED], 0);
+        g_signal_emit (session, signals [ACCREDITED], 0, service_name);
 }
 
 void
 _gdm_session_accreditation_failed (GdmSession   *session,
+                                   const char   *service_name,
                                    const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [ACCREDITATION_FAILED], 0, text);
+        g_signal_emit (session, signals [ACCREDITATION_FAILED], 0, service_name, text);
 }
 
 void
 _gdm_session_info_query (GdmSession   *session,
+                         const char   *service_name,
                          const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [INFO_QUERY], 0, text);
+        g_signal_emit (session, signals [INFO_QUERY], 0, service_name, text);
 }
 
 void
 _gdm_session_secret_info_query (GdmSession   *session,
+                                const char   *service_name,
                                 const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [SECRET_INFO_QUERY], 0, text);
+        g_signal_emit (session, signals [SECRET_INFO_QUERY], 0, service_name, text);
 }
 
 void
 _gdm_session_info (GdmSession   *session,
+                   const char   *service_name,
                    const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [INFO], 0, text);
+        g_signal_emit (session, signals [INFO], 0, service_name, text);
 }
 
 void
 _gdm_session_problem (GdmSession   *session,
+                      const char   *service_name,
                       const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [PROBLEM], 0, text);
+        g_signal_emit (session, signals [PROBLEM], 0, service_name, text);
 }
 
 void
 _gdm_session_session_started (GdmSession   *session,
+                              const char   *service_name,
                               int           pid)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [SESSION_STARTED], 0, pid);
+        g_signal_emit (session, signals [SESSION_STARTED], 0, service_name, pid);
 }
 
 void
 _gdm_session_session_start_failed (GdmSession   *session,
+                                   const char   *service_name,
                                    const char   *text)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [SESSION_START_FAILED], 0, text);
+        g_signal_emit (session, signals [SESSION_START_FAILED], 0, service_name, text);
 }
 
 void
@@ -608,10 +650,19 @@ _gdm_session_session_died (GdmSession   
 }
 
 void
-_gdm_session_opened (GdmSession   *session)
+_gdm_session_conversation_started (GdmSession   *session,
+                                   const char   *service_name)
+{
+        g_return_if_fail (GDM_IS_SESSION (session));
+        g_signal_emit (session, signals [CONVERSATION_STARTED], 0, service_name);
+}
+
+void
+_gdm_session_conversation_stopped (GdmSession   *session,
+                                   const char   *service_name)
 {
         g_return_if_fail (GDM_IS_SESSION (session));
-        g_signal_emit (session, signals [OPENED], 0);
+        g_signal_emit (session, signals [CONVERSATION_STOPPED], 0, service_name);
 }
 
 void
diff -up gdm-2.25.2/daemon/gdm-session-direct.c.multistack-but-boring gdm-2.25.2/daemon/gdm-session-direct.c
--- gdm-2.25.2/daemon/gdm-session-direct.c.multistack-but-boring	2009-03-04 21:03:53.078442376 -0500
+++ gdm-2.25.2/daemon/gdm-session-direct.c	2009-03-04 21:03:53.115432163 -0500
@@ -63,6 +63,16 @@
 #define GDM_SESSION_DEFAULT_PATH "/usr/local/bin:/usr/bin:/bin"
 #endif
 
+typedef struct
+{
+        GdmSessionDirect    *session;
+        GdmSessionWorkerJob *job;
+        GPid                 worker_pid;
+        char                *service_name;
+        DBusConnection      *worker_connection;
+        DBusMessage         *message_pending_reply;
+} GdmSessionConversation;
+
 struct _GdmSessionDirectPrivate
 {
         /* per open scope */
@@ -75,13 +85,13 @@ struct _GdmSessionDirectPrivate
         char                *selected_user;
         char                *user_x11_authority_file;
 
-        DBusMessage         *message_pending_reply;
-        DBusConnection      *worker_connection;
+        GHashTable          *conversations;
+
+        GList               *pending_connections;
 
-        GdmSessionWorkerJob *job;
-        GPid                 session_pid;
         guint32              is_authenticated : 1;
         guint32              is_running : 1;
+        GPid                 session_pid;
 
         /* object lifetime scope */
         char                *id;
@@ -118,39 +128,39 @@ G_DEFINE_TYPE_WITH_CODE (GdmSessionDirec
                                                 gdm_session_iface_init))
 
 static gboolean
-send_dbus_message (DBusConnection *connection,
-                   DBusMessage    *message)
+send_dbus_message (GdmSessionConversation *conversation,
+                   DBusMessage            *message)
 {
         gboolean is_connected;
         gboolean sent;
 
         g_return_val_if_fail (message != NULL, FALSE);
 
-        if (connection == NULL) {
+        if (conversation->worker_connection == NULL) {
                 g_warning ("There is no valid connection");
                 return FALSE;
         }
 
-        is_connected = dbus_connection_get_is_connected (connection);
+        is_connected = dbus_connection_get_is_connected (conversation->worker_connection);
         if (! is_connected) {
                 g_warning ("Not connected!");
                 return FALSE;
         }
 
-        sent = dbus_connection_send (connection, message, NULL);
+        sent = dbus_connection_send (conversation->worker_connection, message, NULL);
 
         return sent;
 }
 
 static void
-send_dbus_string_signal (GdmSessionDirect *session,
+send_dbus_string_signal (GdmSessionConversation *conversation,
                          const char *name,
                          const char *text)
 {
         DBusMessage    *message;
         DBusMessageIter iter;
 
-        g_return_if_fail (session != NULL);
+        g_return_if_fail (conversation != NULL);
 
         message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
                                            GDM_SESSION_DBUS_INTERFACE,
@@ -159,7 +169,7 @@ send_dbus_string_signal (GdmSessionDirec
         dbus_message_iter_init_append (message, &iter);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &text);
 
-        if (! send_dbus_message (session->priv->worker_connection, message)) {
+        if (! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", name);
         }
 
@@ -167,57 +177,90 @@ send_dbus_string_signal (GdmSessionDirec
 }
 
 static void
-send_dbus_void_signal (GdmSessionDirect *session,
-                       const char       *name)
+send_dbus_void_signal (GdmSessionConversation *conversation,
+                       const char             *name)
 {
         DBusMessage *message;
 
-        g_return_if_fail (session != NULL);
+        g_return_if_fail (conversation != NULL);
 
         message = dbus_message_new_signal (GDM_SESSION_DBUS_PATH,
                                            GDM_SESSION_DBUS_INTERFACE,
                                            name);
 
-        if (! send_dbus_message (session->priv->worker_connection, message)) {
+        if (! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", name);
         }
 
         dbus_message_unref (message);
 }
 
+static GdmSessionConversation *
+find_conversation_by_name (GdmSessionDirect *session,
+                           const char       *service_name)
+{
+        GdmSessionConversation *conversation;
+
+        conversation = g_hash_table_lookup (session->priv->conversations, service_name);
+
+        if (conversation == NULL) {
+                g_warning ("Tried to look up non-existant conversation");
+        }
+
+        return conversation;
+}
+
 static void
 on_authentication_failed (GdmSession *session,
+                          const char *service_name,
                           const char *message)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-        gdm_session_record_failed (impl->priv->session_pid,
-                                   impl->priv->selected_user,
-                                   impl->priv->display_hostname,
-                                   impl->priv->display_name,
-                                   impl->priv->display_device);
+        GdmSessionConversation *conversation;
+
+        conversation = find_conversation_by_name (impl, service_name);
+        if (conversation != NULL) {
+                gdm_session_record_failed (conversation->worker_pid,
+                                           impl->priv->selected_user,
+                                           impl->priv->display_hostname,
+                                           impl->priv->display_name,
+                                           impl->priv->display_device);
+        }
 }
 
 static void
-on_session_started (GdmSession *session)
+on_session_started (GdmSession *session,
+                    const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-        gdm_session_record_login (impl->priv->session_pid,
-                                  impl->priv->selected_user,
-                                  impl->priv->display_hostname,
-                                  impl->priv->display_name,
-                                  impl->priv->display_device);
+        GdmSessionConversation *conversation;
+
+        conversation = find_conversation_by_name (impl, service_name);
+        if (conversation != NULL) {
+                gdm_session_record_login (conversation->worker_pid,
+                                          impl->priv->selected_user,
+                                          impl->priv->display_hostname,
+                                          impl->priv->display_name,
+                                          impl->priv->display_device);
+        }
 }
 
 static void
 on_session_start_failed (GdmSession *session,
+                         const char *service_name,
                          const char *message)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-        gdm_session_record_login (impl->priv->session_pid,
-                                  impl->priv->selected_user,
-                                  impl->priv->display_hostname,
-                                  impl->priv->display_name,
-                                  impl->priv->display_device);
+        GdmSessionConversation *conversation;
+
+        conversation = find_conversation_by_name (impl, service_name);
+        if (conversation != NULL) {
+                gdm_session_record_login (conversation->worker_pid,
+                                          impl->priv->selected_user,
+                                          impl->priv->display_hostname,
+                                          impl->priv->display_name,
+                                          impl->priv->display_device);
+        }
 }
 
 static void
@@ -225,6 +268,7 @@ on_session_exited (GdmSession *session,
                    int        exit_code)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+
         gdm_session_record_logout (impl->priv->session_pid,
                                    impl->priv->selected_user,
                                    impl->priv->display_hostname,
@@ -234,7 +278,7 @@ on_session_exited (GdmSession *session,
 
 static DBusHandlerResult
 gdm_session_direct_handle_setup_complete (GdmSessionDirect *session,
-                                          DBusConnection   *connection,
+                                          GdmSessionConversation *conversation,
                                           DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -242,17 +286,17 @@ gdm_session_direct_handle_setup_complete
         g_debug ("GdmSessionDirect: Emitting 'setup-complete' signal");
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_setup_complete (GDM_SESSION (session));
+        _gdm_session_setup_complete (GDM_SESSION (session), conversation->service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_setup_failed (GdmSessionDirect *session,
-                                        DBusConnection   *connection,
+                                        GdmSessionConversation *conversation,
                                         DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -267,12 +311,12 @@ gdm_session_direct_handle_setup_failed (
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'setup-failed' signal");
 
-        _gdm_session_setup_failed (GDM_SESSION (session), NULL);
+        _gdm_session_setup_failed (GDM_SESSION (session), conversation->service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -280,7 +324,7 @@ gdm_session_direct_handle_setup_failed (
 
 static DBusHandlerResult
 gdm_session_direct_handle_reset_complete (GdmSessionDirect *session,
-                                          DBusConnection   *connection,
+                                          GdmSessionConversation *conversation,
                                           DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -288,7 +332,7 @@ gdm_session_direct_handle_reset_complete
         g_debug ("GdmSessionDirect: Emitting 'reset-complete' signal");
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         _gdm_session_reset_complete (GDM_SESSION (session));
@@ -298,7 +342,7 @@ gdm_session_direct_handle_reset_complete
 
 static DBusHandlerResult
 gdm_session_direct_handle_reset_failed (GdmSessionDirect *session,
-                                        DBusConnection   *connection,
+                                        GdmSessionConversation *conversation,
                                         DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -313,7 +357,7 @@ gdm_session_direct_handle_reset_failed (
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'reset-failed' signal");
@@ -325,7 +369,7 @@ gdm_session_direct_handle_reset_failed (
 
 static DBusHandlerResult
 gdm_session_direct_handle_authenticated (GdmSessionDirect *session,
-                                         DBusConnection   *connection,
+                                         GdmSessionConversation *conversation,
                                          DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -333,17 +377,17 @@ gdm_session_direct_handle_authenticated 
         g_debug ("GdmSessionDirect: Emitting 'authenticated' signal");
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_authenticated (GDM_SESSION (session));
+        _gdm_session_authenticated (GDM_SESSION (session), conversation->service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_authentication_failed (GdmSessionDirect *session,
-                                                 DBusConnection   *connection,
+                                                 GdmSessionConversation *conversation,
                                                  DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -358,19 +402,19 @@ gdm_session_direct_handle_authentication
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'authentication-failed' signal");
 
-        _gdm_session_authentication_failed (GDM_SESSION (session), NULL);
+        _gdm_session_authentication_failed (GDM_SESSION (session), conversation->service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_authorized (GdmSessionDirect *session,
-                                      DBusConnection   *connection,
+                                      GdmSessionConversation *conversation,
                                       DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -378,17 +422,17 @@ gdm_session_direct_handle_authorized (Gd
         g_debug ("GdmSessionDirect: Emitting 'authorized' signal");
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_authorized (GDM_SESSION (session));
+        _gdm_session_authorized (GDM_SESSION (session), conversation->service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_authorization_failed (GdmSessionDirect *session,
-                                                DBusConnection   *connection,
+                                                GdmSessionConversation *conversation,
                                                 DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -403,19 +447,19 @@ gdm_session_direct_handle_authorization_
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'authorization-failed' signal");
 
-        _gdm_session_authorization_failed (GDM_SESSION (session), NULL);
+        _gdm_session_authorization_failed (GDM_SESSION (session), conversation->service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_accredited (GdmSessionDirect *session,
-                                      DBusConnection   *connection,
+                                      GdmSessionConversation *conversation,
                                       DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -423,17 +467,17 @@ gdm_session_direct_handle_accredited (Gd
         g_debug ("GdmSessionDirect: Emitting 'accredited' signal");
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_accredited (GDM_SESSION (session));
+        _gdm_session_accredited (GDM_SESSION (session), conversation->service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_accreditation_failed (GdmSessionDirect *session,
-                                                DBusConnection   *connection,
+                                                GdmSessionConversation *conversation,
                                                 DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -448,12 +492,12 @@ gdm_session_direct_handle_accreditation_
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'accreditation-failed' signal");
 
-        _gdm_session_accreditation_failed (GDM_SESSION (session), NULL);
+        _gdm_session_accreditation_failed (GDM_SESSION (session), conversation->service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -753,7 +797,7 @@ gdm_session_direct_select_user (GdmSessi
 
 static DBusHandlerResult
 gdm_session_direct_handle_username_changed (GdmSessionDirect *session,
-                                            DBusConnection   *connection,
+                                            GdmSessionConversation *conversation,
                                             DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -768,7 +812,7 @@ gdm_session_direct_handle_username_chang
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: changing username from '%s' to '%s'",
@@ -785,59 +829,61 @@ gdm_session_direct_handle_username_chang
 }
 
 static void
-cancel_pending_query (GdmSessionDirect *session)
+cancel_pending_query (GdmSessionConversation *conversation)
 {
         DBusMessage *reply;
 
-        if (session->priv->message_pending_reply == NULL) {
+        if (conversation->message_pending_reply == NULL) {
                 return;
         }
 
         g_debug ("GdmSessionDirect: Cancelling pending query");
 
-        reply = dbus_message_new_error (session->priv->message_pending_reply,
+        reply = dbus_message_new_error (conversation->message_pending_reply,
                                         GDM_SESSION_DBUS_ERROR_CANCEL,
                                         "Operation cancelled");
-        dbus_connection_send (session->priv->worker_connection, reply, NULL);
-        dbus_connection_flush (session->priv->worker_connection);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
+        dbus_connection_flush (conversation->worker_connection);
 
         dbus_message_unref (reply);
-        dbus_message_unref (session->priv->message_pending_reply);
-        session->priv->message_pending_reply = NULL;
+        dbus_message_unref (conversation->message_pending_reply);
+        conversation->message_pending_reply = NULL;
 }
 
 static void
 answer_pending_query (GdmSessionDirect *session,
+                      const char       *service_name,
                       const char       *answer)
 {
         DBusMessage    *reply;
         DBusMessageIter iter;
+        GdmSessionConversation *conversation;
 
-        g_assert (session->priv->message_pending_reply != NULL);
+        conversation = find_conversation_by_name (session, service_name);
 
-        reply = dbus_message_new_method_return (session->priv->message_pending_reply);
+        reply = dbus_message_new_method_return (conversation->message_pending_reply);
         dbus_message_iter_init_append (reply, &iter);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &answer);
 
-        dbus_connection_send (session->priv->worker_connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
-        dbus_message_unref (session->priv->message_pending_reply);
-        session->priv->message_pending_reply = NULL;
+        dbus_message_unref (conversation->message_pending_reply);
+        conversation->message_pending_reply = NULL;
 }
 
 static void
-set_pending_query (GdmSessionDirect *session,
-                   DBusMessage      *message)
+set_pending_query (GdmSessionConversation *conversation,
+                   DBusMessage            *message)
 {
-        g_assert (session->priv->message_pending_reply == NULL);
+        g_assert (conversation->message_pending_reply == NULL);
 
-        session->priv->message_pending_reply = dbus_message_ref (message);
+        conversation->message_pending_reply = dbus_message_ref (message);
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_info_query (GdmSessionDirect *session,
-                                      DBusConnection   *connection,
+                                      GdmSessionConversation *conversation,
                                       DBusMessage      *message)
 {
         DBusError    error;
@@ -850,17 +896,17 @@ gdm_session_direct_handle_info_query (Gd
                 g_warning ("ERROR: %s", error.message);
         }
 
-        set_pending_query (session, message);
+        set_pending_query (conversation, message);
 
         g_debug ("GdmSessionDirect: Emitting 'info-query' signal");
-        _gdm_session_info_query (GDM_SESSION (session), text);
+        _gdm_session_info_query (GDM_SESSION (session), conversation->service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_secret_info_query (GdmSessionDirect *session,
-                                             DBusConnection   *connection,
+                                             GdmSessionConversation *conversation,
                                              DBusMessage      *message)
 {
         DBusError    error;
@@ -873,17 +919,17 @@ gdm_session_direct_handle_secret_info_qu
                 g_warning ("ERROR: %s", error.message);
         }
 
-        set_pending_query (session, message);
+        set_pending_query (conversation, message);
 
         g_debug ("GdmSessionDirect: Emitting 'secret-info-query' signal");
-        _gdm_session_secret_info_query (GDM_SESSION (session), text);
+        _gdm_session_secret_info_query (GDM_SESSION (session), conversation->service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_info (GdmSessionDirect *session,
-                                DBusConnection   *connection,
+                                GdmSessionConversation *conversation,
                                 DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -898,27 +944,27 @@ gdm_session_direct_handle_info (GdmSessi
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'info' signal");
-        _gdm_session_info (GDM_SESSION (session), text);
+        _gdm_session_info (GDM_SESSION (session), conversation->service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_cancel_pending_query (GdmSessionDirect *session,
-                                                DBusConnection   *connection,
+                                                GdmSessionConversation *conversation,
                                                 DBusMessage      *message)
 {
         DBusMessage *reply;
 
         g_debug ("GdmSessionDirect: worker cancelling pending query");
-        cancel_pending_query (session);
+        cancel_pending_query (conversation);
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         return DBUS_HANDLER_RESULT_HANDLED;
@@ -926,7 +972,7 @@ gdm_session_direct_handle_cancel_pending
 
 static DBusHandlerResult
 gdm_session_direct_handle_problem (GdmSessionDirect *session,
-                                   DBusConnection   *connection,
+                                   GdmSessionConversation *conversation,
                                    DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -941,18 +987,18 @@ gdm_session_direct_handle_problem (GdmSe
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'problem' signal");
-        _gdm_session_problem (GDM_SESSION (session), text);
+        _gdm_session_problem (GDM_SESSION (session), conversation->service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_session_started (GdmSessionDirect *session,
-                                           DBusConnection   *connection,
+                                           GdmSessionConversation *conversation,
                                            DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -971,7 +1017,7 @@ gdm_session_direct_handle_session_starte
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'session-started' signal with pid '%d'",
@@ -980,14 +1026,14 @@ gdm_session_direct_handle_session_starte
         session->priv->session_pid = pid;
         session->priv->is_running = TRUE;
 
-        _gdm_session_session_started (GDM_SESSION (session), pid);
+        _gdm_session_session_started (GDM_SESSION (session), conversation->service_name, pid);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_start_failed (GdmSessionDirect *session,
-                                        DBusConnection   *connection,
+                                        GdmSessionConversation *conversation,
                                         DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1002,18 +1048,18 @@ gdm_session_direct_handle_start_failed (
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'session-start-failed' signal");
-        _gdm_session_session_start_failed (GDM_SESSION (session), text);
+        _gdm_session_session_start_failed (GDM_SESSION (session), conversation->service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
 static DBusHandlerResult
 gdm_session_direct_handle_session_exited (GdmSessionDirect *session,
-                                          DBusConnection   *connection,
+                                          GdmSessionConversation *conversation,
                                           DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1028,7 +1074,7 @@ gdm_session_direct_handle_session_exited
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'session-exited' signal with exit code '%d'",
@@ -1042,7 +1088,7 @@ gdm_session_direct_handle_session_exited
 
 static DBusHandlerResult
 gdm_session_direct_handle_session_died (GdmSessionDirect *session,
-                                        DBusConnection   *connection,
+                                        GdmSessionConversation *conversation,
                                         DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1057,7 +1103,7 @@ gdm_session_direct_handle_session_died (
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         g_debug ("GdmSessionDirect: Emitting 'session-died' signal with signal number '%d'",
@@ -1071,7 +1117,7 @@ gdm_session_direct_handle_session_died (
 
 static DBusHandlerResult
 gdm_session_direct_handle_saved_language_name_read (GdmSessionDirect *session,
-                                                    DBusConnection   *connection,
+                                                    GdmSessionConversation *conversation,
                                                     DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1086,7 +1132,7 @@ gdm_session_direct_handle_saved_language
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         if (strcmp (language_name,
@@ -1104,7 +1150,7 @@ gdm_session_direct_handle_saved_language
 
 static DBusHandlerResult
 gdm_session_direct_handle_saved_layout_name_read (GdmSessionDirect *session,
-                                                  DBusConnection   *connection,
+                                                  GdmSessionConversation *conversation,
                                                   DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1119,7 +1165,7 @@ gdm_session_direct_handle_saved_layout_n
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         if (strcmp (layout_name,
@@ -1137,7 +1183,7 @@ gdm_session_direct_handle_saved_layout_n
 
 static DBusHandlerResult
 gdm_session_direct_handle_saved_session_name_read (GdmSessionDirect *session,
-                                                   DBusConnection   *connection,
+                                                   GdmSessionConversation *conversation,
                                                    DBusMessage      *message)
 {
         DBusMessage *reply;
@@ -1152,7 +1198,7 @@ gdm_session_direct_handle_saved_session_
         }
 
         reply = dbus_message_new_method_return (message);
-        dbus_connection_send (connection, reply, NULL);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
         dbus_message_unref (reply);
 
         if (! get_session_command_for_name (session_name, NULL)) {
@@ -1180,66 +1226,69 @@ session_worker_message (DBusConnection *
                         DBusMessage    *message,
                         void           *user_data)
 {
-        GdmSessionDirect *session = GDM_SESSION_DIRECT (user_data);
+        GdmSessionConversation *conversation = user_data;
+        GdmSessionDirect *session;
+
+        session = conversation->session;
 
         if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "InfoQuery")) {
-                return gdm_session_direct_handle_info_query (session, connection, message);
+                return gdm_session_direct_handle_info_query (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SecretInfoQuery")) {
-                return gdm_session_direct_handle_secret_info_query (session, connection, message);
+                return gdm_session_direct_handle_secret_info_query (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Info")) {
-                return gdm_session_direct_handle_info (session, connection, message);
+                return gdm_session_direct_handle_info (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Problem")) {
-                return gdm_session_direct_handle_problem (session, connection, message);
+                return gdm_session_direct_handle_problem (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "CancelPendingQuery")) {
-                return gdm_session_direct_handle_cancel_pending_query (session, connection, message);
+                return gdm_session_direct_handle_cancel_pending_query (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SetupComplete")) {
-                return gdm_session_direct_handle_setup_complete (session, connection, message);
+                return gdm_session_direct_handle_setup_complete (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SetupFailed")) {
-                return gdm_session_direct_handle_setup_failed (session, connection, message);
+                return gdm_session_direct_handle_setup_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "ResetComplete")) {
-                return gdm_session_direct_handle_reset_complete (session, connection, message);
+                return gdm_session_direct_handle_reset_complete (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "ResetFailed")) {
-                return gdm_session_direct_handle_reset_failed (session, connection, message);
+                return gdm_session_direct_handle_reset_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Authenticated")) {
-                return gdm_session_direct_handle_authenticated (session, connection, message);
+                return gdm_session_direct_handle_authenticated (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AuthenticationFailed")) {
-                return gdm_session_direct_handle_authentication_failed (session, connection, message);
+                return gdm_session_direct_handle_authentication_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Authorized")) {
-                return gdm_session_direct_handle_authorized (session, connection, message);
+                return gdm_session_direct_handle_authorized (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AuthorizationFailed")) {
-                return gdm_session_direct_handle_authorization_failed (session, connection, message);
+                return gdm_session_direct_handle_authorization_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Accredited")) {
-                return gdm_session_direct_handle_accredited (session, connection, message);
+                return gdm_session_direct_handle_accredited (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AccreditationFailed")) {
-                return gdm_session_direct_handle_accreditation_failed (session, connection, message);
+                return gdm_session_direct_handle_accreditation_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Authenticated")) {
-                return gdm_session_direct_handle_authenticated (session, connection, message);
+                return gdm_session_direct_handle_authenticated (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AuthenticationFailed")) {
-                return gdm_session_direct_handle_authentication_failed (session, connection, message);
+                return gdm_session_direct_handle_authentication_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Authorized")) {
-                return gdm_session_direct_handle_authorized (session, connection, message);
+                return gdm_session_direct_handle_authorized (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AuthorizationFailed")) {
-                return gdm_session_direct_handle_authorization_failed (session, connection, message);
+                return gdm_session_direct_handle_authorization_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Accredited")) {
-                return gdm_session_direct_handle_accredited (session, connection, message);
+                return gdm_session_direct_handle_accredited (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "AccreditationFailed")) {
-                return gdm_session_direct_handle_accreditation_failed (session, connection, message);
+                return gdm_session_direct_handle_accreditation_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "UsernameChanged")) {
-                return gdm_session_direct_handle_username_changed (session, connection, message);
+                return gdm_session_direct_handle_username_changed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionStarted")) {
-                return gdm_session_direct_handle_session_started (session, connection, message);
+                return gdm_session_direct_handle_session_started (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "StartFailed")) {
-                return gdm_session_direct_handle_start_failed (session, connection, message);
+                return gdm_session_direct_handle_start_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionExited")) {
-                return gdm_session_direct_handle_session_exited (session, connection, message);
+                return gdm_session_direct_handle_session_exited (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionDied")) {
-                return gdm_session_direct_handle_session_died (session, connection, message);
+                return gdm_session_direct_handle_session_died (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SavedLanguageNameRead")) {
-                return gdm_session_direct_handle_saved_language_name_read (session, connection, message);
+                return gdm_session_direct_handle_saved_language_name_read (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SavedLayoutNameRead")) {
-                return gdm_session_direct_handle_saved_layout_name_read (session, connection, message);
+                return gdm_session_direct_handle_saved_layout_name_read (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SavedSessionNameRead")) {
-                return gdm_session_direct_handle_saved_session_name_read (session, connection, message);
+                return gdm_session_direct_handle_saved_session_name_read (session, conversation, message);
         }
 
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -1473,6 +1522,27 @@ session_unregister_handler (DBusConnecti
         g_debug ("session_unregister_handler");
 }
 
+static GdmSessionConversation *
+find_conversation_by_pid (GdmSessionDirect *session,
+                          GPid              pid)
+{
+        GHashTableIter iter;
+        gpointer key, value;
+
+        g_hash_table_iter_init (&iter, session->priv->conversations);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                GdmSessionConversation *conversation;
+
+                conversation = (GdmSessionConversation *) value;
+
+                if (conversation->worker_pid == pid) {
+                        return conversation;
+                }
+        }
+
+        return NULL;
+}
+
 static dbus_bool_t
 allow_user_function (DBusConnection *connection,
                      unsigned long   uid,
@@ -1487,41 +1557,108 @@ allow_user_function (DBusConnection *con
         return FALSE;
 }
 
+static gboolean
+register_worker (GdmSessionDirect *session,
+                 DBusConnection   *connection)
+{
+        GdmSessionConversation *conversation;
+        DBusObjectPathVTable vtable = { &session_unregister_handler,
+                                        &session_message_handler,
+                                        NULL, NULL, NULL, NULL };
+        GList *connection_node;
+        gulong pid;
+
+        g_debug ("GdmSessionDirect: Authenticating new connection");
+
+        connection_node = g_list_find (session->priv->pending_connections, connection);
+
+        if (connection_node == NULL) {
+                g_debug ("GdmSessionDirect: Ignoring connection that we aren't tracking");
+                return FALSE;
+        }
+
+        session->priv->pending_connections =
+                g_list_delete_link (session->priv->pending_connections,
+                                    connection_node);
+
+        if (!dbus_connection_get_unix_process_id (connection, &pid)) {
+                g_warning ("GdmSessionDirect: Unable to read pid on new worker connection");
+                dbus_connection_unref (connection);
+                return FALSE;
+        }
+
+        conversation = find_conversation_by_pid (session, (GPid) pid);
+
+        if (conversation == NULL) {
+                g_warning ("GdmSessionDirect: New worker connection is from unknown source");
+                dbus_connection_unref (connection);
+                return FALSE;
+        }
+
+        conversation->worker_connection = connection;
+
+        g_debug ("GdmSessionDirect: worker connection is %p", connection);
+
+        dbus_connection_register_object_path (connection,
+                                              GDM_SESSION_DBUS_PATH,
+                                              &vtable,
+                                              conversation);
+
+        g_debug ("GdmSessionDirect: Emitting conversation-started signal");
+        _gdm_session_conversation_started (GDM_SESSION (session),
+                                           conversation->service_name);
+
+        g_debug ("GdmSessionDirect: Conversation started");
+
+        return TRUE;
+}
+
+static DBusHandlerResult
+on_message (DBusConnection *connection, DBusMessage *message, void *user_data)
+{
+        GdmSessionDirect *session = GDM_SESSION_DIRECT (user_data);
+
+        g_debug ("GdmSessionDirect: got message");
+
+        if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "Hello")) {
+                DBusMessage *reply;
+
+                if (register_worker (session, connection)) {
+                        reply = dbus_message_new_method_return (message);
+                } else {
+                        reply = dbus_message_new_error (message, DBUS_ERROR_FAILED, "");
+                }
+
+                dbus_connection_send (connection, reply, NULL);
+                return DBUS_HANDLER_RESULT_HANDLED;
+        }
+
+        return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+}
+
 static void
 handle_connection (DBusServer      *server,
                    DBusConnection  *new_connection,
                    void            *user_data)
 {
         GdmSessionDirect *session = GDM_SESSION_DIRECT (user_data);
-
         g_debug ("GdmSessionDirect: Handing new connection");
 
-        if (session->priv->worker_connection == NULL) {
-                DBusObjectPathVTable vtable = { &session_unregister_handler,
-                                                &session_message_handler,
-                                                NULL, NULL, NULL, NULL
-                };
-
-                session->priv->worker_connection = new_connection;
-                dbus_connection_ref (new_connection);
-                dbus_connection_setup_with_g_main (new_connection, NULL);
-
-                g_debug ("GdmSessionDirect: worker connection is %p", new_connection);
-                dbus_connection_set_exit_on_disconnect (new_connection, FALSE);
-
-                dbus_connection_set_unix_user_function (new_connection,
-                                                        allow_user_function,
-                                                        session,
-                                                        NULL);
-
-                dbus_connection_register_object_path (new_connection,
-                                                      GDM_SESSION_DBUS_PATH,
-                                                      &vtable,
-                                                      session);
-
-                g_debug ("GdmSessionDirect: Emitting opened signal");
-                _gdm_session_opened (GDM_SESSION (session));
-        }
+        /* add to the list of pending connections.  We won't be able to
+         * associate it with a specific worker conversation until we have
+         * authenticated the connection (from the Hello handler).
+         */
+        session->priv->pending_connections =
+                g_list_prepend (session->priv->pending_connections,
+                                dbus_connection_ref (new_connection));
+        dbus_connection_setup_with_g_main (new_connection, NULL);
+        dbus_connection_set_exit_on_disconnect (new_connection, FALSE);
+
+        dbus_connection_set_unix_user_function (new_connection,
+                                                allow_user_function,
+                                                session,
+                                                NULL);
+        dbus_connection_add_filter (new_connection, on_message, session, NULL);
 }
 
 static gboolean
@@ -1567,6 +1704,17 @@ setup_server (GdmSessionDirect *session)
 }
 
 static void
+free_conversation (GdmSessionConversation *conversation)
+{
+        if (conversation->job != NULL) {
+                g_warning ("Freeing conversation with active job");
+        }
+
+        g_free (conversation->service_name);
+        g_free (conversation);
+}
+
+static void
 gdm_session_direct_init (GdmSessionDirect *session)
 {
         session->priv = G_TYPE_INSTANCE_GET_PRIVATE (session,
@@ -1590,8 +1738,11 @@ gdm_session_direct_init (GdmSessionDirec
                           G_CALLBACK (on_session_exited),
                           NULL);
 
-        session->priv->session_pid = -1;
-
+        session->priv->conversations = g_hash_table_new_full (g_str_hash,
+                                                              g_str_equal,
+                                                              (GDestroyNotify) g_free,
+                                                              (GDestroyNotify)
+                                                              free_conversation);
         session->priv->environment = g_hash_table_new_full (g_str_hash,
                                                             g_str_equal,
                                                             (GDestroyNotify) g_free,
@@ -1602,15 +1753,8 @@ gdm_session_direct_init (GdmSessionDirec
 }
 
 static void
-worker_stopped (GdmSessionWorkerJob *job,
-                GdmSessionDirect    *session)
-{
-        g_debug ("GdmSessionDirect: Worker job stopped");
-}
-
-static void
 worker_started (GdmSessionWorkerJob *job,
-                GdmSessionDirect    *session)
+                GdmSessionConversation *conversation)
 {
         g_debug ("GdmSessionDirect: Worker job started");
 }
@@ -1618,106 +1762,161 @@ worker_started (GdmSessionWorkerJob *job
 static void
 worker_exited (GdmSessionWorkerJob *job,
                int                  code,
-               GdmSessionDirect    *session)
+               GdmSessionConversation *conversation)
 {
         g_debug ("GdmSessionDirect: Worker job exited: %d", code);
 
-        if (!session->priv->is_authenticated) {
+        g_object_ref (conversation);
+        if (!conversation->session->priv->is_authenticated) {
                 char *msg;
 
                 msg = g_strdup_printf (_("worker exited with status %d"), code);
-                _gdm_session_authentication_failed (GDM_SESSION (session), msg);
+                _gdm_session_authentication_failed (GDM_SESSION (conversation->session), conversation->service_name, msg);
                 g_free (msg);
-        } else if (session->priv->is_running) {
-                _gdm_session_session_exited (GDM_SESSION (session), code);
+        } else if (conversation->session->priv->is_running) {
+                _gdm_session_session_exited (GDM_SESSION (conversation->session), code);
         }
+
+        g_debug ("GdmSessionDirect: Emitting conversation-stopped signal");
+        _gdm_session_conversation_stopped (GDM_SESSION (conversation->session),
+                                           conversation->service_name);
+        g_object_unref (conversation);
 }
 
 static void
 worker_died (GdmSessionWorkerJob *job,
              int                  signum,
-             GdmSessionDirect    *session)
+             GdmSessionConversation *conversation)
 {
         g_debug ("GdmSessionDirect: Worker job died: %d", signum);
 
-        if (!session->priv->is_authenticated) {
+        g_object_ref (conversation);
+        if (!conversation->session->priv->is_authenticated) {
                 char *msg;
 
                 msg = g_strdup_printf (_("worker exited with status %d"), signum);
-                _gdm_session_authentication_failed (GDM_SESSION (session), msg);
+                _gdm_session_authentication_failed (GDM_SESSION (conversation->session), conversation->service_name, msg);
                 g_free (msg);
-        } else if (session->priv->is_running) {
-                _gdm_session_session_died (GDM_SESSION (session), signum);
+        } else if (conversation->session->priv->is_running) {
+                _gdm_session_session_died (GDM_SESSION (conversation->session), signum);
         }
-}
-
-static gboolean
-start_worker (GdmSessionDirect *session)
-{
-        gboolean res;
 
-        session->priv->job = gdm_session_worker_job_new ();
-        gdm_session_worker_job_set_server_address (session->priv->job, session->priv->server_address);
-        g_signal_connect (session->priv->job,
-                          "stopped",
-                          G_CALLBACK (worker_stopped),
-                          session);
-        g_signal_connect (session->priv->job,
+        g_debug ("GdmSessionDirect: Emitting conversation-stopped signal");
+        _gdm_session_conversation_stopped (GDM_SESSION (conversation->session),
+                                           conversation->service_name);
+        g_object_unref (conversation);
+}
+
+static GdmSessionConversation *
+start_conversation (GdmSessionDirect *session,
+                    const char       *service_name)
+{
+        GdmSessionConversation *conversation;
+        char                   *job_name;
+
+        conversation = g_new0 (GdmSessionConversation, 1);
+        conversation->session = session;
+        conversation->service_name = g_strdup (service_name);
+        conversation->worker_pid = -1;
+        conversation->job = gdm_session_worker_job_new ();
+        gdm_session_worker_job_set_server_address (conversation->job, session->priv->server_address);
+        g_signal_connect (conversation->job,
                           "started",
                           G_CALLBACK (worker_started),
-                          session);
-        g_signal_connect (session->priv->job,
+                          conversation);
+        g_signal_connect (conversation->job,
                           "exited",
                           G_CALLBACK (worker_exited),
-                          session);
-        g_signal_connect (session->priv->job,
+                          conversation);
+        g_signal_connect (conversation->job,
                           "died",
                           G_CALLBACK (worker_died),
-                          session);
+                          conversation);
 
-        res = gdm_session_worker_job_start (session->priv->job);
+        job_name = g_strdup_printf ("pam: %s", service_name);
+        if (!gdm_session_worker_job_start (conversation->job,
+                                           job_name)) {
+                g_object_unref (conversation->job);
+                g_free (conversation->service_name);
+                g_free (conversation);
+                g_free (job_name);
+                return NULL;
+        }
+        g_free (job_name);
 
-        return res;
+        conversation->worker_pid = gdm_session_worker_job_get_pid (conversation->job);
+
+        return conversation;
 }
 
 static void
-stop_worker (GdmSessionDirect *session)
+stop_conversation (GdmSessionConversation *conversation)
 {
-        g_signal_handlers_disconnect_by_func (session->priv->job,
-                                              G_CALLBACK (worker_stopped),
-                                              session);
-        g_signal_handlers_disconnect_by_func (session->priv->job,
+        GdmSessionDirect *session;
+
+        session = conversation->session;
+
+        g_signal_handlers_disconnect_by_func (conversation->job,
                                               G_CALLBACK (worker_started),
-                                              session);
-        g_signal_handlers_disconnect_by_func (session->priv->job,
+                                              conversation);
+        g_signal_handlers_disconnect_by_func (conversation->job,
                                               G_CALLBACK (worker_exited),
-                                              session);
-        g_signal_handlers_disconnect_by_func (session->priv->job,
+                                              conversation);
+        g_signal_handlers_disconnect_by_func (conversation->job,
                                               G_CALLBACK (worker_died),
-                                              session);
+                                              conversation);
 
-        cancel_pending_query (session);
+        if (conversation->worker_connection != NULL) {
+                dbus_connection_remove_filter (conversation->worker_connection, on_message, session);
 
-        if (session->priv->worker_connection != NULL) {
-                dbus_connection_close (session->priv->worker_connection);
-                session->priv->worker_connection = NULL;
+                dbus_connection_close (conversation->worker_connection);
+                conversation->worker_connection = NULL;
         }
 
-        gdm_session_worker_job_stop (session->priv->job);
-        g_object_unref (session->priv->job);
-        session->priv->job = NULL;
+        gdm_session_worker_job_stop (conversation->job);
+
+        g_object_unref (conversation->job);
+        conversation->job = NULL;
+
+        g_debug ("GdmSessionDirect: Emitting conversation-stopped signal");
+        _gdm_session_conversation_stopped (GDM_SESSION (session),
+                                           conversation->service_name);
 }
 
 static void
-gdm_session_direct_open (GdmSession *session)
+gdm_session_direct_start_conversation (GdmSession *session,
+                                       const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_return_if_fail (session != NULL);
 
-        g_debug ("GdmSessionDirect: Opening session");
+        g_debug ("GdmSessionDirect: starting conversation");
 
-        start_worker (impl);
+        conversation = start_conversation (impl, service_name);
+
+        g_hash_table_insert (impl->priv->conversations,
+                             g_strdup (service_name), conversation);
+}
+
+static void
+gdm_session_direct_stop_conversation (GdmSession *session,
+                                      const char *service_name)
+{
+        GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
+
+        g_return_if_fail (session != NULL);
+
+        g_debug ("GdmSessionDirect: stopping conversation");
+
+        conversation = find_conversation_by_name (impl, service_name);
+
+        if (conversation != NULL) {
+                stop_conversation (conversation);
+                g_hash_table_remove (impl->priv->conversations, service_name);
+        }
 }
 
 static void
@@ -1730,6 +1929,7 @@ send_setup (GdmSessionDirect *session,
         const char     *display_device;
         const char     *display_hostname;
         const char     *display_x11_authority_file;
+        GdmSessionConversation *conversation;
 
         g_assert (service_name != NULL);
 
@@ -1767,7 +1967,8 @@ send_setup (GdmSessionDirect *session,
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &display_hostname);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &display_x11_authority_file);
 
-        if (! send_dbus_message (session->priv->worker_connection, message)) {
+        conversation = find_conversation_by_name (session, service_name);
+        if (conversation != NULL && ! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", "Setup");
         }
 
@@ -1785,6 +1986,7 @@ send_setup_for_user (GdmSessionDirect *s
         const char     *display_hostname;
         const char     *display_x11_authority_file;
         const char     *selected_user;
+        GdmSessionConversation *conversation;
 
         g_assert (service_name != NULL);
 
@@ -1828,7 +2030,8 @@ send_setup_for_user (GdmSessionDirect *s
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &display_x11_authority_file);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &selected_user);
 
-        if (! send_dbus_message (session->priv->worker_connection, message)) {
+        conversation = find_conversation_by_name (session, service_name);
+        if (conversation != NULL && ! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", "SetupForUser");
         }
 
@@ -1842,7 +2045,6 @@ gdm_session_direct_setup (GdmSession *se
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
 
         g_return_if_fail (session != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->worker_connection));
 
         send_setup (impl, service_name);
         gdm_session_direct_defaults_changed (impl);
@@ -1856,7 +2058,6 @@ gdm_session_direct_setup_for_user (GdmSe
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
 
         g_return_if_fail (session != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->worker_connection));
         g_return_if_fail (username != NULL);
 
         gdm_session_direct_select_user (session, username);
@@ -1866,42 +2067,56 @@ gdm_session_direct_setup_for_user (GdmSe
 }
 
 static void
-gdm_session_direct_authenticate (GdmSession *session)
+gdm_session_direct_authenticate (GdmSession *session,
+                                 const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_return_if_fail (session != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->worker_connection));
 
-        send_dbus_void_signal (impl, "Authenticate");
+        conversation = find_conversation_by_name (impl, service_name);
+        if (conversation != NULL) {
+                send_dbus_void_signal (conversation, "Authenticate");
+        }
 }
 
 static void
-gdm_session_direct_authorize (GdmSession *session)
+gdm_session_direct_authorize (GdmSession *session,
+                              const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_return_if_fail (session != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->worker_connection));
 
-        send_dbus_void_signal (impl, "Authorize");
+        conversation = find_conversation_by_name (impl, service_name);
+        if (conversation != NULL) {
+                send_dbus_void_signal (conversation, "Authorize");
+        }
 }
 
 static void
 gdm_session_direct_accredit (GdmSession *session,
+                             const char *service_name,
                              int         cred_flag)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
 
         g_return_if_fail (session != NULL);
-        g_return_if_fail (dbus_connection_get_is_connected (impl->priv->worker_connection));
+
+        conversation = find_conversation_by_name (impl, service_name);
+        if (conversation == NULL) {
+                return;
+        }
 
         switch (cred_flag) {
         case GDM_SESSION_CRED_ESTABLISH:
-                send_dbus_void_signal (impl, "EstablishCredentials");
+                send_dbus_void_signal (conversation, "EstablishCredentials");
                 break;
         case GDM_SESSION_CRED_REFRESH:
-                send_dbus_void_signal (impl, "RefreshCredentials");
+                send_dbus_void_signal (conversation, "RefreshCredentials");
                 break;
         default:
                 g_assert_not_reached ();
@@ -1909,9 +2124,9 @@ gdm_session_direct_accredit (GdmSession 
 }
 
 static void
-send_environment_variable (const char       *key,
-                           const char       *value,
-                           GdmSessionDirect *session)
+send_environment_variable (const char             *key,
+                           const char             *value,
+                           GdmSessionConversation *conversation)
 {
         DBusMessage    *message;
         DBusMessageIter iter;
@@ -1924,7 +2139,7 @@ send_environment_variable (const char   
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &key);
         dbus_message_iter_append_basic (&iter, DBUS_TYPE_STRING, &value);
 
-        if (! send_dbus_message (session->priv->worker_connection, message)) {
+        if (! send_dbus_message (conversation, message)) {
                 g_debug ("GdmSessionDirect: Could not send %s signal", "SetEnvironmentVariable");
         }
 
@@ -1932,12 +2147,13 @@ send_environment_variable (const char   
 }
 
 static void
-send_environment (GdmSessionDirect *session)
+send_environment (GdmSessionDirect       *session,
+                  GdmSessionConversation *conversation)
 {
 
         g_hash_table_foreach (session->priv->environment,
                               (GHFunc) send_environment_variable,
-                              session);
+                              conversation);
 }
 
 static const char *
@@ -2052,9 +2268,11 @@ setup_session_environment (GdmSessionDir
 }
 
 static void
-gdm_session_direct_start_session (GdmSession *session)
+gdm_session_direct_start_session (GdmSession *session,
+                                  const char *service_name)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
         char             *command;
         char             *program;
 
@@ -2065,14 +2283,38 @@ gdm_session_direct_start_session (GdmSes
         program = g_strdup_printf (GDMCONFDIR "/Xsession \"%s\"", command);
         g_free (command);
 
+        conversation = find_conversation_by_name (impl, service_name);
+
         setup_session_environment (impl);
-        send_environment (impl);
+        send_environment (impl, conversation);
 
-        send_dbus_string_signal (impl, "StartProgram", program);
+        send_dbus_string_signal (conversation, "StartProgram", program);
         g_free (program);
 }
 
 static void
+stop_all_conversations (GdmSessionDirect *session)
+{
+        GHashTableIter iter;
+        gpointer key, value;
+
+        if (session->priv->conversations == NULL) {
+                return;
+        }
+
+        g_hash_table_iter_init (&iter, session->priv->conversations);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                GdmSessionConversation *conversation;
+
+                conversation = (GdmSessionConversation *) value;
+
+                stop_conversation (conversation);
+        }
+
+        g_hash_table_remove_all (session->priv->conversations);
+}
+
+static void
 gdm_session_direct_close (GdmSession *session)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
@@ -2081,18 +2323,21 @@ gdm_session_direct_close (GdmSession *se
 
         g_debug ("GdmSessionDirect: Closing session");
 
-        if (impl->priv->job != NULL) {
-                if (impl->priv->is_running) {
-                        gdm_session_record_logout (impl->priv->session_pid,
-                                                   impl->priv->selected_user,
-                                                   impl->priv->display_hostname,
-                                                   impl->priv->display_name,
-                                                   impl->priv->display_device);
-                }
-
-                stop_worker (impl);
+        if (impl->priv->is_running) {
+                gdm_session_record_logout (impl->priv->session_pid,
+                                           impl->priv->selected_user,
+                                           impl->priv->display_hostname,
+                                           impl->priv->display_name,
+                                           impl->priv->display_device);
         }
 
+        stop_all_conversations (impl);
+
+        g_list_foreach (impl->priv->pending_connections,
+                        (GFunc) dbus_connection_unref, NULL);
+        g_list_free (impl->priv->pending_connections);
+        impl->priv->pending_connections = NULL;
+
         g_free (impl->priv->selected_user);
         impl->priv->selected_user = NULL;
 
@@ -2119,30 +2364,28 @@ gdm_session_direct_close (GdmSession *se
 
         g_hash_table_remove_all (impl->priv->environment);
 
-        impl->priv->session_pid = -1;
         impl->priv->is_authenticated = FALSE;
         impl->priv->is_running = FALSE;
 }
 
 static void
 gdm_session_direct_answer_query  (GdmSession *session,
+                                  const char *service_name,
                                   const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
 
         g_return_if_fail (session != NULL);
 
-        answer_pending_query (impl, text);
+        answer_pending_query (impl, service_name, text);
 }
 
 static void
 gdm_session_direct_cancel  (GdmSession *session)
 {
-        GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
-
         g_return_if_fail (session != NULL);
 
-        cancel_pending_query (impl);
+        stop_all_conversations (GDM_SESSION_DIRECT (session));
 }
 
 char *
@@ -2158,6 +2401,8 @@ gdm_session_direct_select_session (GdmSe
                                    const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GHashTableIter iter;
+        gpointer key, value;
 
         g_free (impl->priv->selected_session);
 
@@ -2167,8 +2412,15 @@ gdm_session_direct_select_session (GdmSe
                 impl->priv->selected_session = g_strdup (text);
         }
 
-        send_dbus_string_signal (impl, "SetSessionName",
-                                 get_session_name (impl));
+        g_hash_table_iter_init (&iter, impl->priv->conversations);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                GdmSessionConversation *conversation;
+
+                conversation = (GdmSessionConversation *) value;
+
+                send_dbus_string_signal (conversation, "SetSessionName",
+                                         get_session_name (impl));
+        }
 }
 
 static void
@@ -2176,6 +2428,8 @@ gdm_session_direct_select_language (GdmS
                                     const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GHashTableIter iter;
+        gpointer key, value;
 
         g_free (impl->priv->selected_language);
 
@@ -2185,8 +2439,15 @@ gdm_session_direct_select_language (GdmS
                 impl->priv->selected_language = g_strdup (text);
         }
 
-        send_dbus_string_signal (impl, "SetLanguageName",
-                                 get_language_name (impl));
+        g_hash_table_iter_init (&iter, impl->priv->conversations);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                GdmSessionConversation *conversation;
+
+                conversation = (GdmSessionConversation *) value;
+
+                send_dbus_string_signal (conversation, "SetLanguageName",
+                                         get_language_name (impl));
+        }
 }
 
 static void
@@ -2194,6 +2455,8 @@ gdm_session_direct_select_layout (GdmSes
                                   const char *text)
 {
         GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GHashTableIter iter;
+        gpointer key, value;
 
         g_free (impl->priv->selected_layout);
 
@@ -2203,8 +2466,15 @@ gdm_session_direct_select_layout (GdmSes
                 impl->priv->selected_layout = g_strdup (text);
         }
 
-        send_dbus_string_signal (impl, "SetLayoutName",
-                                 get_layout_name (impl));
+        g_hash_table_iter_init (&iter, impl->priv->conversations);
+        while (g_hash_table_iter_next (&iter, &key, &value)) {
+                GdmSessionConversation *conversation;
+
+                conversation = (GdmSessionConversation *) value;
+
+                send_dbus_string_signal (conversation, "SetLayoutName",
+                                         get_language_name (impl));
+        }
 }
 
 static void
@@ -2463,7 +2733,8 @@ gdm_session_direct_constructor (GType   
 static void
 gdm_session_iface_init (GdmSessionIface *iface)
 {
-        iface->open = gdm_session_direct_open;
+        iface->start_conversation = gdm_session_direct_start_conversation;
+        iface->stop_conversation = gdm_session_direct_stop_conversation;
         iface->setup = gdm_session_direct_setup;
         iface->setup_for_user = gdm_session_direct_setup_for_user;
         iface->authenticate = gdm_session_direct_authenticate;
diff -up gdm-2.25.2/daemon/gdm-session.h.multistack-but-boring gdm-2.25.2/daemon/gdm-session.h
--- gdm-2.25.2/daemon/gdm-session.h.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-session.h	2009-03-04 21:03:53.116432427 -0500
@@ -45,18 +45,25 @@ struct _GdmSessionIface
         GTypeInterface base_iface;
 
         /* Methods */
-        void (* open)                        (GdmSession   *session);
+        void (* start_conversation)          (GdmSession   *session,
+                                              const char   *service_name);
+        void (* stop_conversation)           (GdmSession   *session,
+                                              const char   *service_name);
         void (* setup)                       (GdmSession   *session,
                                               const char   *service_name);
         void (* setup_for_user)              (GdmSession   *session,
                                               const char   *service_name,
                                               const char   *username);
         void (* reset)                       (GdmSession   *session);
-        void (* authenticate)                (GdmSession   *session);
-        void (* authorize)                   (GdmSession   *session);
+        void (* authenticate)                (GdmSession   *session,
+                                              const char   *service_name);
+        void (* authorize)                   (GdmSession   *session,
+                                              const char   *service_name);
         void (* accredit)                    (GdmSession   *session,
+                                              const char   *service_name,
                                               int           cred_flag);
         void (* answer_query)                (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *text);
         void (* select_language)             (GdmSession   *session,
                                               const char   *text);
@@ -66,44 +73,62 @@ struct _GdmSessionIface
                                               const char   *text);
         void (* select_user)                 (GdmSession   *session,
                                               const char   *text);
-        void (* start_session)               (GdmSession   *session);
+        void (* start_session)               (GdmSession   *session,
+                                              const char   *service_name);
         void (* close)                       (GdmSession   *session);
         void (* cancel)                      (GdmSession   *session);
 
         /* Signals */
-        void (* setup_complete)              (GdmSession   *session);
+        void (* setup_complete)              (GdmSession   *session,
+                                              const char   *service_name);
         void (* setup_failed)                (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *message);
         void (* reset_complete)              (GdmSession   *session);
         void (* reset_failed)                (GdmSession   *session,
                                               const char   *message);
-        void (* authenticated)               (GdmSession   *session);
+        void (* authenticated)               (GdmSession   *session,
+                                              const char   *service_name);
         void (* authentication_failed)       (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *message);
-        void (* authorized)                  (GdmSession   *session);
+        void (* authorized)                  (GdmSession   *session,
+                                              const char   *service_name);
         void (* authorization_failed)        (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *message);
-        void (* accredited)                  (GdmSession   *session);
+        void (* accredited)                  (GdmSession   *session,
+                                              const char   *service_name);
         void (* accreditation_failed)        (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *message);
 
         void (* info_query)                  (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *query_text);
         void (* secret_info_query)           (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *query_text);
         void (* info)                        (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *info);
         void (* problem)                     (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *problem);
         void (* session_started)             (GdmSession   *session,
+                                              const char   *service_name,
                                               int           pid);
         void (* session_start_failed)        (GdmSession   *session,
+                                              const char   *service_name,
                                               const char   *message);
         void (* session_exited)              (GdmSession   *session,
                                               int           exit_code);
         void (* session_died)                (GdmSession   *session,
                                               int           signal_number);
-        void (* opened)                      (GdmSession   *session);
+        void (* conversation_started)        (GdmSession   *session,
+                                              const char   *service_name);
+        void (* conversation_stopped)        (GdmSession   *session,
+                                              const char   *service_name);
         void (* closed)                      (GdmSession   *session);
         void (* selected_user_changed)       (GdmSession   *session,
                                               const char   *text);
@@ -118,21 +143,29 @@ struct _GdmSessionIface
 
 GType    gdm_session_get_type                    (void) G_GNUC_CONST;
 
-void     gdm_session_open                        (GdmSession *session);
+void     gdm_session_start_conversation          (GdmSession *session,
+                                                  const char *service_name);
+void     gdm_session_stop_conversation           (GdmSession *session,
+                                                  const char *service_name);
 void     gdm_session_setup                       (GdmSession *session,
                                                   const char *service_name);
 void     gdm_session_setup_for_user              (GdmSession *session,
                                                   const char *service_name,
                                                   const char *username);
 void     gdm_session_reset                       (GdmSession *session);
-void     gdm_session_authenticate                (GdmSession *session);
-void     gdm_session_authorize                   (GdmSession *session);
+void     gdm_session_authenticate                (GdmSession *session,
+                                                  const char *service_name);
+void     gdm_session_authorize                   (GdmSession *session,
+                                                  const char *service_name);
 void     gdm_session_accredit                    (GdmSession *session,
+                                                  const char *service_name,
                                                   int         cred_flag);
-void     gdm_session_start_session               (GdmSession *session);
+void     gdm_session_start_session               (GdmSession *session,
+                                                  const char *service_name);
 void     gdm_session_close                       (GdmSession *session);
 
 void     gdm_session_answer_query                (GdmSession *session,
+                                                  const char *service_name,
                                                   const char *text);
 void     gdm_session_select_session              (GdmSession *session,
                                                   const char *session_name);
diff -up gdm-2.25.2/daemon/gdm-session-private.h.multistack-but-boring gdm-2.25.2/daemon/gdm-session-private.h
--- gdm-2.25.2/daemon/gdm-session-private.h.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-session-private.h	2009-03-04 21:03:53.118432814 -0500
@@ -27,25 +27,38 @@
 G_BEGIN_DECLS
 
 /* state changes */
-void             _gdm_session_opened                       (GdmSession   *session);
-void             _gdm_session_setup_complete               (GdmSession   *session);
+void             _gdm_session_conversation_started         (GdmSession   *session,
+                                                            const char   *service_name);
+void             _gdm_session_conversation_stopped         (GdmSession   *session,
+                                                            const char   *service_name);
+void             _gdm_session_setup_complete               (GdmSession   *session,
+                                                            const char   *service_name);
 void             _gdm_session_setup_failed                 (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *message);
 void             _gdm_session_reset_complete               (GdmSession   *session);
 void             _gdm_session_reset_failed                 (GdmSession   *session,
                                                             const char   *message);
-void             _gdm_session_authenticated                (GdmSession   *session);
+void             _gdm_session_authenticated                (GdmSession   *session,
+                                                            const char   *service_name);
 void             _gdm_session_authentication_failed        (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *text);
-void             _gdm_session_authorized                   (GdmSession   *session);
+void             _gdm_session_authorized                   (GdmSession   *session,
+                                                            const char   *service_name);
 void             _gdm_session_authorization_failed         (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *text);
-void             _gdm_session_accredited                   (GdmSession   *session);
+void             _gdm_session_accredited                   (GdmSession   *session,
+                                                            const char   *service_name);
 void             _gdm_session_accreditation_failed         (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *text);
 void             _gdm_session_session_started              (GdmSession   *session,
+                                                            const char   *service_name,
                                                             int           pid);
 void             _gdm_session_session_start_failed         (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *message);
 void             _gdm_session_session_exited               (GdmSession   *session,
                                                             int           exit_code);
@@ -66,12 +79,16 @@ void             _gdm_session_selected_u
 
 /* call and response stuff */
 void             _gdm_session_info_query                   (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *text);
 void             _gdm_session_secret_info_query            (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *text);
 void             _gdm_session_info                         (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *text);
 void             _gdm_session_problem                      (GdmSession   *session,
+                                                            const char   *service_name,
                                                             const char   *text);
 
 G_END_DECLS
diff -up gdm-2.25.2/daemon/gdm-session-relay.c.multistack-but-boring gdm-2.25.2/daemon/gdm-session-relay.c
--- gdm-2.25.2/daemon/gdm-session-relay.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-session-relay.c	2009-03-04 21:03:53.120433133 -0500
@@ -180,10 +180,11 @@ send_dbus_void_signal (GdmSessionRelay *
 }
 
 static void
-gdm_session_relay_open (GdmSession *session)
+gdm_session_relay_start_conversation (GdmSession *session,
+                                      const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_void_signal (impl, "Open");
+        send_dbus_string_signal (impl, "StartConversation", service_name);
 }
 
 static void
@@ -211,31 +212,34 @@ gdm_session_relay_setup_for_user (GdmSes
 }
 
 static void
-gdm_session_relay_authenticate (GdmSession *session)
+gdm_session_relay_authenticate (GdmSession *session,
+                                const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_void_signal (impl, "Authenticate");
+        send_dbus_string_signal (impl, "Authenticate", service_name);
 }
 
 static void
-gdm_session_relay_authorize (GdmSession *session)
+gdm_session_relay_authorize (GdmSession *session,
+                             const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_void_signal (impl, "Authorize");
+        send_dbus_string_signal (impl, "Authorize", service_name);
 }
 
 static void
 gdm_session_relay_accredit (GdmSession *session,
+                            const char *service_name,
                             int         cred_flag)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
 
         switch (cred_flag) {
         case GDM_SESSION_CRED_ESTABLISH:
-                send_dbus_void_signal (impl, "EstablishCredentials");
+                send_dbus_string_signal (impl, "EstablishCredentials", service_name);
                 break;
         case GDM_SESSION_CRED_REFRESH:
-                send_dbus_void_signal (impl, "RefreshCredentials");
+                send_dbus_string_signal (impl, "RefreshCredentials", service_name);
                 break;
         default:
                 g_assert_not_reached ();
@@ -244,10 +248,11 @@ gdm_session_relay_accredit (GdmSession *
 
 static void
 gdm_session_relay_answer_query (GdmSession *session,
+                                const char *service_name,
                                 const char *text)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
-        send_dbus_string_signal (impl, "AnswerQuery", text);
+        send_dbus_string_string_signal (impl, "AnswerQuery", service_name, text);
 }
 
 static void
@@ -291,11 +296,12 @@ gdm_session_relay_cancel (GdmSession *se
 }
 
 static void
-gdm_session_relay_start_session (GdmSession *session)
+gdm_session_relay_start_session (GdmSession *session,
+                                 const char *service_name)
 {
         GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
 
-        send_dbus_void_signal (impl, "StartSession");
+        send_dbus_string_signal (impl, "StartSession", service_name);
 }
 
 /* Note: Use abstract sockets like dbus does by default on Linux. Abstract
@@ -333,10 +339,12 @@ handle_info_query (GdmSessionRelay *sess
 {
         DBusMessage *reply;
         DBusError    error;
-        const char  *text;
+        char  *service_name;
+        char  *text;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_STRING, &text,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
@@ -348,7 +356,7 @@ handle_info_query (GdmSessionRelay *sess
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_info_query (GDM_SESSION (session_relay), text);
+        _gdm_session_info_query (GDM_SESSION (session_relay), service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -360,12 +368,14 @@ handle_secret_info_query (GdmSessionRela
 {
         DBusMessage *reply;
         DBusError    error;
-        const char  *text;
+        char        *service_name;
+        char  *text;
 
         text = NULL;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_STRING, &text,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
@@ -377,7 +387,7 @@ handle_secret_info_query (GdmSessionRela
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_secret_info_query (GDM_SESSION (session_relay), text);
+        _gdm_session_secret_info_query (GDM_SESSION (session_relay), service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -389,12 +399,14 @@ handle_info (GdmSessionRelay *session_re
 {
         DBusMessage *reply;
         DBusError    error;
-        const char  *text;
+        char        *service_name;
+        char        *text;
 
         text = NULL;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_STRING, &text,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
@@ -406,7 +418,7 @@ handle_info (GdmSessionRelay *session_re
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_info (GDM_SESSION (session_relay), text);
+        _gdm_session_info (GDM_SESSION (session_relay), service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -418,12 +430,14 @@ handle_problem (GdmSessionRelay *session
 {
         DBusMessage *reply;
         DBusError    error;
-        const char  *text;
+        char        *service_name;
+        char        *text;
 
         text = NULL;
 
         dbus_error_init (&error);
         if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_STRING, &text,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
@@ -435,7 +449,7 @@ handle_problem (GdmSessionRelay *session
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_problem (GDM_SESSION (session_relay), text);
+        _gdm_session_problem (GDM_SESSION (session_relay), service_name, text);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -447,8 +461,15 @@ handle_setup_complete (GdmSessionRelay *
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: SetupComplete");
 
@@ -456,7 +477,7 @@ handle_setup_complete (GdmSessionRelay *
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_setup_complete (GDM_SESSION (session_relay));
+        _gdm_session_setup_complete (GDM_SESSION (session_relay), service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -468,8 +489,15 @@ handle_setup_failed (GdmSessionRelay *se
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: SetupFailed");
 
@@ -477,7 +505,7 @@ handle_setup_failed (GdmSessionRelay *se
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_setup_failed (GDM_SESSION (session_relay), NULL);
+        _gdm_session_setup_failed (GDM_SESSION (session_relay), service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -490,8 +518,15 @@ handle_authenticated (GdmSessionRelay *s
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: Authenticated");
 
@@ -499,7 +534,7 @@ handle_authenticated (GdmSessionRelay *s
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_authenticated (GDM_SESSION (session_relay));
+        _gdm_session_authenticated (GDM_SESSION (session_relay), service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -511,8 +546,15 @@ handle_authentication_failed (GdmSession
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: AuthenticationFailed");
 
@@ -520,7 +562,7 @@ handle_authentication_failed (GdmSession
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_authentication_failed (GDM_SESSION (session_relay), NULL);
+        _gdm_session_authentication_failed (GDM_SESSION (session_relay), service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -532,8 +574,15 @@ handle_authorized (GdmSessionRelay *sess
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: Authorized");
 
@@ -541,7 +590,7 @@ handle_authorized (GdmSessionRelay *sess
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_authorized (GDM_SESSION (session_relay));
+        _gdm_session_authorized (GDM_SESSION (session_relay), service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -553,8 +602,15 @@ handle_authorization_failed (GdmSessionR
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: AuthorizationFailed");
 
@@ -562,7 +618,7 @@ handle_authorization_failed (GdmSessionR
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_authorization_failed (GDM_SESSION (session_relay), NULL);
+        _gdm_session_authorization_failed (GDM_SESSION (session_relay), service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -574,8 +630,15 @@ handle_accredited (GdmSessionRelay *sess
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: Accredited");
 
@@ -583,7 +646,7 @@ handle_accredited (GdmSessionRelay *sess
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_accredited (GDM_SESSION (session_relay));
+        _gdm_session_accredited (GDM_SESSION (session_relay), service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -595,8 +658,15 @@ handle_accreditation_failed (GdmSessionR
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
 
         g_debug ("GdmSessionRelay: AccreditationFailed");
 
@@ -604,7 +674,7 @@ handle_accreditation_failed (GdmSessionR
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_accreditation_failed (GDM_SESSION (session_relay), NULL);
+        _gdm_session_accreditation_failed (GDM_SESSION (session_relay), service_name, NULL);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -616,6 +686,7 @@ handle_session_started (GdmSessionRelay 
 {
         DBusMessage *reply;
         DBusError    error;
+        char        *service_name;
         int          pid;
 
         dbus_error_init (&error);
@@ -623,6 +694,7 @@ handle_session_started (GdmSessionRelay 
         pid = 0;
         if (! dbus_message_get_args (message,
                                      &error,
+                                     DBUS_TYPE_STRING, &service_name,
                                      DBUS_TYPE_INT32, &pid,
                                      DBUS_TYPE_INVALID)) {
                 g_warning ("ERROR: %s", error.message);
@@ -635,6 +707,7 @@ handle_session_started (GdmSessionRelay 
         dbus_message_unref (reply);
 
         _gdm_session_session_started (GDM_SESSION (session_relay),
+                                      service_name,
                                       pid);
 
         return DBUS_HANDLER_RESULT_HANDLED;
@@ -664,22 +737,28 @@ handle_session_stopped (GdmSessionRelay 
 }
 
 static DBusHandlerResult
-handle_opened (GdmSessionRelay *session_relay,
+handle_conversation_started (GdmSessionRelay *session_relay,
                DBusConnection  *connection,
                DBusMessage     *message)
 {
         DBusMessage *reply;
         DBusError    error;
+        char *service_name;
 
         dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
 
-        g_debug ("GdmSessionRelay: Opened");
+        g_debug ("GdmSessionRelay: Conversation Started");
 
         reply = dbus_message_new_method_return (message);
         dbus_connection_send (connection, reply, NULL);
         dbus_message_unref (reply);
 
-        _gdm_session_opened (GDM_SESSION (session_relay));
+        _gdm_session_conversation_started (GDM_SESSION (session_relay), service_name);
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
@@ -719,8 +798,8 @@ session_handle_child_message (DBusConnec
                 return handle_session_started (session_relay, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionStopped")) {
                 return handle_session_stopped (session_relay, connection, message);
-        } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "Opened")) {
-                return handle_opened (session_relay, connection, message);
+        } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "ConversationStarted")) {
+                return handle_conversation_started (session_relay, connection, message);
         }
 
         return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
@@ -749,7 +828,8 @@ do_introspect (DBusConnection *connectio
         /* interface */
         xml = g_string_append (xml,
                                "  <interface name=\"org.gnome.DisplayManager.SessionRelay\">\n"
-                               "    <method name=\"Opened\">\n"
+                               "    <method name=\"ConversationStarted\">\n"
+                               "      <arg name=\"service_name\" direction=\"in\" type=\"s\"/>\n"
                                "    </method>\n"
                                "    <method name=\"SetupComplete\">\n"
                                "    </method>\n"
@@ -810,7 +890,8 @@ do_introspect (DBusConnection *connectio
                                "    <signal name=\"RefreshCredentials\">\n"
                                "    </signal>\n"
 
-                               "    <signal name=\"Open\">\n"
+                               "    <signal name=\"StartConversation\">\n"
+                               "      <arg name=\"service_name\" type=\"s\"/>\n"
                                "    </signal>\n"
                                "    <signal name=\"Close\">\n"
                                "    </signal>\n"
@@ -1106,7 +1187,7 @@ static void
 gdm_session_iface_init (GdmSessionIface *iface)
 {
 
-        iface->open = gdm_session_relay_open;
+        iface->start_conversation = gdm_session_relay_start_conversation;
         iface->setup = gdm_session_relay_setup;
         iface->setup_for_user = gdm_session_relay_setup_for_user;
         iface->authenticate = gdm_session_relay_authenticate;
diff -up gdm-2.25.2/daemon/gdm-session-worker.c.multistack-but-boring gdm-2.25.2/daemon/gdm-session-worker.c
--- gdm-2.25.2/daemon/gdm-session-worker.c.multistack-but-boring	2008-11-18 17:19:05.000000000 -0500
+++ gdm-2.25.2/daemon/gdm-session-worker.c	2009-03-05 16:51:38.010621245 -0500
@@ -2054,8 +2054,6 @@ do_setup (GdmSessionWorker *worker)
         GError  *error;
         gboolean res;
 
-        worker->priv->user_settings = gdm_session_settings_new ();
-
         g_signal_connect_swapped (worker->priv->user_settings,
                                   "notify::language-name",
                                   G_CALLBACK (on_saved_language_name_read),
@@ -2568,6 +2566,28 @@ worker_dbus_filter_function (DBusConnect
         return DBUS_HANDLER_RESULT_HANDLED;
 }
 
+static void
+send_hello (GdmSessionWorker *worker)
+{
+        DBusMessage *message, *reply;
+        DBusError error;
+
+        message = dbus_message_new_method_call (NULL,
+                                                GDM_SESSION_DBUS_PATH,
+                                                GDM_SESSION_DBUS_INTERFACE,
+                                                "Hello");
+
+        dbus_error_init (&error);
+        reply = dbus_connection_send_with_reply_and_block (worker->priv->connection,
+                                                           message, -1, &error);
+        dbus_message_unref (message);
+        dbus_error_free (&error);
+
+        if (reply != NULL) {
+                dbus_message_unref (reply);
+        }
+}
+
 static GObject *
 gdm_session_worker_constructor (GType                  type,
                                 guint                  n_construct_properties,
@@ -2594,6 +2614,11 @@ gdm_session_worker_constructor (GType   
                 exit (1);
         }
 
+        /* Send an initial Hello message so that the session can associate
+         * the conversation we manage with our pid.
+         */
+        send_hello (worker);
+
         dbus_connection_setup_with_g_main (worker->priv->connection, NULL);
         dbus_connection_set_exit_on_disconnect (worker->priv->connection, TRUE);
 
@@ -2635,6 +2660,7 @@ gdm_session_worker_init (GdmSessionWorke
                                                            g_str_equal,
                                                            (GDestroyNotify) g_free,
                                                            (GDestroyNotify) g_free);
+        worker->priv->user_settings = gdm_session_settings_new ();
 }
 
 static void
diff -up gdm-2.25.2/daemon/gdm-session-worker-job.c.multistack-but-boring gdm-2.25.2/daemon/gdm-session-worker-job.c
--- gdm-2.25.2/daemon/gdm-session-worker-job.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-session-worker-job.c	2009-03-04 21:03:53.125433055 -0500
@@ -68,7 +68,6 @@ enum {
 
 enum {
         STARTED,
-        STOPPED,
         EXITED,
         DIED,
         LAST_SIGNAL
@@ -124,6 +123,37 @@ listify_hash (const char *key,
 }
 
 static GPtrArray *
+get_job_arguments (GdmSessionWorkerJob *job,
+                   const char          *name)
+{
+        GPtrArray  *args;
+        GError     *error;
+        char      **argv;
+        int         i;
+
+        args = NULL;
+        argv = NULL;
+        error = NULL;
+        if (! g_shell_parse_argv (job->priv->command, NULL, &argv, &error)) {
+                g_warning ("Could not parse command: %s", error->message);
+                g_error_free (error);
+                goto out;
+        }
+
+        args = g_ptr_array_new ();
+        g_ptr_array_add (args, g_strdup (argv[0]));
+        g_ptr_array_add (args, g_strdup (name));
+        for (i = 1; argv[i] != NULL; i++) {
+                g_ptr_array_add (args, g_strdup (argv[i]));
+        }
+        g_strfreev (argv);
+
+        g_ptr_array_add (args, NULL);
+out:
+        return args;
+}
+
+static GPtrArray *
 get_job_environment (GdmSessionWorkerJob *job)
 {
         GPtrArray     *env;
@@ -145,31 +175,31 @@ get_job_environment (GdmSessionWorkerJob
 }
 
 static gboolean
-gdm_session_worker_job_spawn (GdmSessionWorkerJob *session_worker_job)
+gdm_session_worker_job_spawn (GdmSessionWorkerJob *session_worker_job,
+                              const char          *name)
 {
-        gchar          **argv;
         GError          *error;
         gboolean         ret;
+        GPtrArray       *args;
         GPtrArray       *env;
 
         ret = FALSE;
 
-        g_debug ("GdmSessionWorkerJob: Running session_worker_job process: %s", session_worker_job->priv->command);
+        g_debug ("GdmSessionWorkerJob: Running session_worker_job process: %s %s",
+                 name != NULL? name : "", session_worker_job->priv->command);
 
-        argv = NULL;
-        if (! g_shell_parse_argv (session_worker_job->priv->command, NULL, &argv, &error)) {
-                g_warning ("Could not parse command: %s", error->message);
-                g_error_free (error);
-                goto out;
-        }
+        args = get_job_arguments (session_worker_job, name);
 
+        if (args == NULL) {
+                return FALSE;
+        }
         env = get_job_environment (session_worker_job);
 
         error = NULL;
         ret = g_spawn_async_with_pipes (NULL,
-                                        argv,
+                                        (char **) args->pdata,
                                         (char **)env->pdata,
-                                        G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD,
+                                        G_SPAWN_SEARCH_PATH | G_SPAWN_DO_NOT_REAP_CHILD | G_SPAWN_FILE_AND_ARGV_ZERO,
                                         (GSpawnChildSetupFunc)session_worker_job_child_setup,
                                         session_worker_job,
                                         &session_worker_job->priv->pid,
@@ -178,6 +208,9 @@ gdm_session_worker_job_spawn (GdmSession
                                         NULL,
                                         &error);
 
+        g_ptr_array_foreach (args, (GFunc)g_free, NULL);
+        g_ptr_array_free (args, TRUE);
+
         g_ptr_array_foreach (env, (GFunc)g_free, NULL);
         g_ptr_array_free (env, TRUE);
 
@@ -194,7 +227,6 @@ gdm_session_worker_job_spawn (GdmSession
                                                                       (GChildWatchFunc)session_worker_job_child_watch,
                                                                       session_worker_job);
 
-        g_strfreev (argv);
  out:
 
         return ret;
@@ -207,13 +239,14 @@ gdm_session_worker_job_spawn (GdmSession
  * Starts a local X session_worker_job. Handles retries and fatal errors properly.
  */
 gboolean
-gdm_session_worker_job_start (GdmSessionWorkerJob *session_worker_job)
+gdm_session_worker_job_start (GdmSessionWorkerJob *session_worker_job,
+                              const char          *name)
 {
         gboolean    res;
 
         g_debug ("GdmSessionWorkerJob: Starting worker...");
 
-        res = gdm_session_worker_job_spawn (session_worker_job);
+        res = gdm_session_worker_job_spawn (session_worker_job, name);
 
         if (res) {
 
@@ -261,6 +294,7 @@ gdm_session_worker_job_stop (GdmSessionW
         g_debug ("GdmSessionWorkerJob: Stopping job pid:%d", session_worker_job->priv->pid);
 
         res = gdm_signal_pid (session_worker_job->priv->pid, SIGTERM);
+
         if (res < 0) {
                 g_warning ("Unable to kill session worker process");
         } else {
@@ -270,6 +304,13 @@ gdm_session_worker_job_stop (GdmSessionW
         return TRUE;
 }
 
+GPid
+gdm_session_worker_job_get_pid (GdmSessionWorkerJob *session_worker_job)
+{
+        g_return_val_if_fail (GDM_IS_SESSION_WORKER_JOB (session_worker_job), 0);
+        return session_worker_job->priv->pid;
+}
+
 void
 gdm_session_worker_job_set_server_address (GdmSessionWorkerJob *session_worker_job,
                                            const char      *address)
@@ -363,16 +404,6 @@ gdm_session_worker_job_class_init (GdmSe
                               g_cclosure_marshal_VOID__VOID,
                               G_TYPE_NONE,
                               0);
-        signals [STOPPED] =
-                g_signal_new ("stopped",
-                              G_OBJECT_CLASS_TYPE (object_class),
-                              G_SIGNAL_RUN_FIRST,
-                              G_STRUCT_OFFSET (GdmSessionWorkerJobClass, stopped),
-                              NULL,
-                              NULL,
-                              g_cclosure_marshal_VOID__VOID,
-                              G_TYPE_NONE,
-                              0);
         signals [EXITED] =
                 g_signal_new ("exited",
                               G_OBJECT_CLASS_TYPE (object_class),
diff -up gdm-2.25.2/daemon/gdm-session-worker-job.h.multistack-but-boring gdm-2.25.2/daemon/gdm-session-worker-job.h
--- gdm-2.25.2/daemon/gdm-session-worker-job.h.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/gdm-session-worker-job.h	2009-03-04 21:03:53.126432201 -0500
@@ -46,7 +46,6 @@ typedef struct
         GObjectClass   parent_class;
 
         void (* started)           (GdmSessionWorkerJob  *session_worker_job);
-        void (* stopped)           (GdmSessionWorkerJob  *session_worker_job);
         void (* exited)            (GdmSessionWorkerJob  *session_worker_job,
                                     int                   exit_code);
 
@@ -58,9 +57,12 @@ GType                   gdm_session_work
 GdmSessionWorkerJob *   gdm_session_worker_job_new                (void);
 void                    gdm_session_worker_job_set_server_address (GdmSessionWorkerJob *session_worker_job,
                                                                    const char          *server_address);
-gboolean                gdm_session_worker_job_start              (GdmSessionWorkerJob *session_worker_job);
+gboolean                gdm_session_worker_job_start              (GdmSessionWorkerJob *session_worker_job,
+                                                                   const char          *name);
 gboolean                gdm_session_worker_job_stop               (GdmSessionWorkerJob *session_worker_job);
 
+GPid                    gdm_session_worker_job_get_pid            (GdmSessionWorkerJob *session_worker_job);
+
 G_END_DECLS
 
 #endif /* __GDM_SESSION_WORKER_JOB_H */
diff -up gdm-2.25.2/daemon/gdm-simple-slave.c.multistack-but-boring gdm-2.25.2/daemon/gdm-simple-slave.c
--- gdm-2.25.2/daemon/gdm-simple-slave.c.multistack-but-boring	2009-03-04 21:03:53.057442982 -0500
+++ gdm-2.25.2/daemon/gdm-simple-slave.c	2009-03-06 15:03:10.290300356 -0500
@@ -68,6 +68,8 @@ struct GdmSimpleSlavePrivate
         guint              greeter_reset_id;
         guint              start_session_id;
 
+        char              *start_session_service_name;
+
         int                ping_interval;
 
         GPid               server_pid;
@@ -99,6 +101,7 @@ static void start_greeter      (GdmSimpl
 
 static void
 on_session_started (GdmSession       *session,
+                    const char       *service_name,
                     int               pid,
                     GdmSimpleSlave   *slave)
 {
@@ -165,6 +168,8 @@ destroy_session (GdmSimpleSlave *slave)
                 g_object_unref (slave->priv->session);
                 slave->priv->session = NULL;
         }
+
+        slave->priv->start_session_when_ready = FALSE;
 }
 
 static void
@@ -172,7 +177,6 @@ reset_session (GdmSimpleSlave *slave)
 {
         destroy_session (slave);
         create_new_session (slave);
-        gdm_session_open (GDM_SESSION (slave->priv->session));
 }
 
 static gboolean
@@ -203,23 +207,25 @@ queue_greeter_reset (GdmSimpleSlave *sla
 
 static void
 on_session_setup_complete (GdmSession     *session,
+                           const char     *service_name,
                            GdmSimpleSlave *slave)
 {
-        gdm_session_authenticate (session);
+        gdm_session_authenticate (session, service_name);
 }
 
 static void
 on_session_setup_failed (GdmSession     *session,
+                         const char     *service_name,
                          const char     *message,
                          GdmSimpleSlave *slave)
 {
         if (slave->priv->greeter_server != NULL) {
                 gdm_greeter_server_problem (slave->priv->greeter_server,
+                                            service_name,
                                             _("Unable to initialize login system"));
         }
 
-        destroy_session (slave);
-        queue_greeter_reset (slave);
+        gdm_session_stop_conversation (session, service_name);
 }
 
 static void
@@ -239,26 +245,30 @@ on_session_reset_failed (GdmSession     
 
 static void
 on_session_authenticated (GdmSession     *session,
+                          const char     *service_name,
                           GdmSimpleSlave *slave)
 {
-        gdm_session_authorize (session);
+        gdm_session_authorize (session, service_name);
 }
 
 static void
 on_session_authentication_failed (GdmSession     *session,
+                                  const char     *service_name,
                                   const char     *message,
                                   GdmSimpleSlave *slave)
 {
         if (slave->priv->greeter_server != NULL) {
                 gdm_greeter_server_problem (slave->priv->greeter_server,
+                                            service_name,
                                             _("Unable to authenticate user"));
         }
-        destroy_session (slave);
-        queue_greeter_reset (slave);
+
+        gdm_session_stop_conversation (session, service_name);
 }
 
 static void
-gdm_simple_slave_accredit_when_ready (GdmSimpleSlave *slave)
+gdm_simple_slave_accredit_when_ready (GdmSimpleSlave *slave,
+                                      const char     *service_name)
 {
         if (slave->priv->start_session_when_ready) {
                 char *ssid;
@@ -279,7 +289,7 @@ gdm_simple_slave_accredit_when_ready (Gd
                 g_free (ssid);
                 g_free (username);
 
-                gdm_session_accredit (GDM_SESSION (slave->priv->session), cred_flag);
+                gdm_session_accredit (GDM_SESSION (slave->priv->session), service_name, cred_flag);
         } else {
                 slave->priv->waiting_to_start_session = TRUE;
         }
@@ -287,29 +297,31 @@ gdm_simple_slave_accredit_when_ready (Gd
 
 static void
 on_session_authorized (GdmSession     *session,
+                       const char     *service_name,
                        GdmSimpleSlave *slave)
 {
         if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_user_authorized (slave->priv->greeter_server);
-                gdm_simple_slave_accredit_when_ready (slave);
+                gdm_greeter_server_user_authorized (slave->priv->greeter_server, service_name);
+                gdm_simple_slave_accredit_when_ready (slave, service_name);
         } else {
                 slave->priv->start_session_when_ready = TRUE;
-                gdm_simple_slave_accredit_when_ready (slave);
+                gdm_simple_slave_accredit_when_ready (slave, service_name);
         }
 }
 
 static void
 on_session_authorization_failed (GdmSession     *session,
+                                 const char     *service_name,
                                  const char     *message,
                                  GdmSimpleSlave *slave)
 {
         if (slave->priv->greeter_server != NULL) {
                 gdm_greeter_server_problem (slave->priv->greeter_server,
+                                            service_name,
                                             _("Unable to authorize user"));
         }
 
-        destroy_session (slave);
-        queue_greeter_reset (slave);
+        gdm_session_stop_conversation (session, service_name);
 }
 
 static gboolean
@@ -385,31 +397,38 @@ start_session_timeout (GdmSimpleSlave *s
 
         g_free (auth_file);
 
-        gdm_session_start_session (GDM_SESSION (slave->priv->session));
+        gdm_session_start_session (GDM_SESSION (slave->priv->session),
+                                   slave->priv->start_session_service_name);
  out:
         slave->priv->start_session_id = 0;
+        g_free (slave->priv->start_session_service_name);
+        slave->priv->start_session_service_name = NULL;
         return FALSE;
 }
 
 static void
-queue_start_session (GdmSimpleSlave *slave)
+queue_start_session (GdmSimpleSlave *slave,
+                     const char     *service_name)
 {
         if (slave->priv->start_session_id > 0) {
                 return;
         }
 
         slave->priv->start_session_id = g_idle_add ((GSourceFunc)start_session_timeout, slave);
+        slave->priv->start_session_service_name = g_strdup (service_name);
 }
 
 static void
 on_session_accredited (GdmSession     *session,
+                       const char     *service_name,
                        GdmSimpleSlave *slave)
 {
-        queue_start_session (slave);
+        queue_start_session (slave, service_name);
 }
 
 static void
 on_session_accreditation_failed (GdmSession     *session,
+                                 const char     *service_name,
                                  const char     *message,
                                  GdmSimpleSlave *slave)
 {
@@ -424,6 +443,7 @@ on_session_accreditation_failed (GdmSess
         if (! migrated) {
                 if (slave->priv->greeter_server != NULL) {
                         gdm_greeter_server_problem (slave->priv->greeter_server,
+                                                    service_name,
                                                     _("Unable establish credentials"));
                 }
         }
@@ -432,62 +452,67 @@ on_session_accreditation_failed (GdmSess
            when Xorg exits it switches to the VT it was
            started from.  That interferes with fast
            user switching. */
-        destroy_session (slave);
 
-        queue_greeter_reset (slave);
+        gdm_session_stop_conversation (session, service_name);
 }
 
 static void
 on_session_info (GdmSession     *session,
+                 const char     *service_name,
                  const char     *text,
                  GdmSimpleSlave *slave)
 {
         g_debug ("GdmSimpleSlave: Info: %s", text);
         if (slave->priv->greeter_server != NULL) {
-                gdm_greeter_server_info (slave->priv->greeter_server, text);
+                gdm_greeter_server_info (slave->priv->greeter_server, service_name, text);
         }
 }
 
 static void
 on_session_problem (GdmSession     *session,
+                    const char     *service_name,
                     const char     *text,
                     GdmSimpleSlave *slave)
 {
         g_debug ("GdmSimpleSlave: Problem: %s", text);
-        gdm_greeter_server_problem (slave->priv->greeter_server, text);
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, text);
 }
 
 static void
 on_session_info_query (GdmSession     *session,
+                       const char     *service_name,
                        const char     *text,
                        GdmSimpleSlave *slave)
 {
 
         g_debug ("GdmSimpleSlave: Info query: %s", text);
-        gdm_greeter_server_info_query (slave->priv->greeter_server, text);
+        gdm_greeter_server_info_query (slave->priv->greeter_server, service_name, text);
 }
 
 static void
 on_session_secret_info_query (GdmSession     *session,
+                              const char     *service_name,
                               const char     *text,
                               GdmSimpleSlave *slave)
 {
         g_debug ("GdmSimpleSlave: Secret info query: %s", text);
-        gdm_greeter_server_secret_info_query (slave->priv->greeter_server, text);
+        gdm_greeter_server_secret_info_query (slave->priv->greeter_server, service_name, text);
 }
 
 static void
-on_session_opened (GdmSession     *session,
-                   GdmSimpleSlave *slave)
+on_session_conversation_started (GdmSession     *session,
+                                 const char     *service_name,
+                                 GdmSimpleSlave *slave)
 {
         gboolean res;
         gboolean enabled;
         char    *username;
         int      delay;
 
-        g_debug ("GdmSimpleSlave: session opened");
+        g_debug ("GdmSimpleSlave: conversation started");
         if (slave->priv->greeter_server != NULL) {
-                res = gdm_greeter_server_ready (slave->priv->greeter_server);
+                res = gdm_greeter_server_ready (slave->priv->greeter_server,
+                                                service_name);
                 if (! res) {
                         g_warning ("Unable to send ready");
                 }
@@ -503,8 +528,10 @@ on_session_opened (GdmSession     *sessi
                 gdm_greeter_server_request_timed_login (slave->priv->greeter_server, username, delay);
         } else {
                 g_debug ("GdmSimpleSlave: begin auto login for user '%s'", username);
+                /* service_name will be "gdm-autologin"
+                 */
                 gdm_session_setup_for_user (GDM_SESSION (slave->priv->session),
-                                            "gdm-autologin",
+                                            service_name,
                                             username);
         }
 
@@ -512,6 +539,23 @@ on_session_opened (GdmSession     *sessi
 }
 
 static void
+on_session_conversation_stopped (GdmSession     *session,
+                                 const char     *service_name,
+                                 GdmSimpleSlave *slave)
+{
+        gboolean res;
+        g_debug ("GdmSimpleSlave: conversation stopped");
+
+        if (slave->priv->greeter_server != NULL) {
+                res = gdm_greeter_server_conversation_stopped (slave->priv->greeter_server,
+                                                               service_name);
+                if (! res) {
+                        g_warning ("Unable to send conversation stopped");
+                }
+        }
+}
+
+static void
 on_session_selected_user_changed (GdmSession     *session,
                                   const char     *text,
                                   GdmSimpleSlave *slave)
@@ -596,8 +640,12 @@ create_new_session (GdmSimpleSlave *slav
         g_free (display_hostname);
 
         g_signal_connect (slave->priv->session,
-                          "opened",
-                          G_CALLBACK (on_session_opened),
+                          "conversation-started",
+                          G_CALLBACK (on_session_conversation_started),
+                          slave);
+        g_signal_connect (slave->priv->session,
+                          "conversation-stopped",
+                          G_CALLBACK (on_session_conversation_stopped),
                           slave);
         g_signal_connect (slave->priv->session,
                           "setup-complete",
@@ -728,12 +776,29 @@ on_greeter_session_died (GdmGreeterSessi
 }
 
 static void
+on_greeter_start_conversation (GdmGreeterServer *greeter_server,
+                               const char       *service_name,
+                               GdmSimpleSlave   *slave)
+{
+        g_debug ("GdmSimpleSlave: starting conversation with '%s' pam service'", service_name);
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
+        gdm_session_start_conversation (GDM_SESSION (slave->priv->session),
+                                        service_name);
+}
+
+static void
 on_greeter_begin_verification (GdmGreeterServer *greeter_server,
+                               const char       *service_name,
                                GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: begin verification");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_setup (GDM_SESSION (slave->priv->session),
-                           "gdm");
+                           service_name);
 }
 
 static void
@@ -742,6 +807,9 @@ on_greeter_begin_auto_login (GdmGreeterS
                              GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: begin auto login for user '%s'", username);
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_setup_for_user (GDM_SESSION (slave->priv->session),
                                     "gdm-autologin",
                                     username);
@@ -749,21 +817,29 @@ on_greeter_begin_auto_login (GdmGreeterS
 
 static void
 on_greeter_begin_verification_for_user (GdmGreeterServer *greeter_server,
+                                        const char       *service_name,
                                         const char       *username,
                                         GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: begin verification");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_setup_for_user (GDM_SESSION (slave->priv->session),
-                                    "gdm",
+                                    service_name,
                                     username);
 }
 
 static void
 on_greeter_answer (GdmGreeterServer *greeter_server,
+                   const char       *service_name,
                    const char       *text,
                    GdmSimpleSlave   *slave)
 {
-        gdm_session_answer_query (GDM_SESSION (slave->priv->session), text);
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
+        gdm_session_answer_query (GDM_SESSION (slave->priv->session), service_name, text);
 }
 
 static void
@@ -771,6 +847,9 @@ on_greeter_session_selected (GdmGreeterS
                              const char       *text,
                              GdmSimpleSlave   *slave)
 {
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_select_session (GDM_SESSION (slave->priv->session), text);
 }
 
@@ -779,6 +858,9 @@ on_greeter_language_selected (GdmGreeter
                               const char       *text,
                               GdmSimpleSlave   *slave)
 {
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_select_language (GDM_SESSION (slave->priv->session), text);
 }
 
@@ -787,6 +869,9 @@ on_greeter_layout_selected (GdmGreeterSe
                             const char       *text,
                             GdmSimpleSlave   *slave)
 {
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         gdm_session_select_layout (GDM_SESSION (slave->priv->session), text);
 }
 
@@ -803,7 +888,11 @@ on_greeter_cancel (GdmGreeterServer *gre
                    GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: Greeter cancelled");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         reset_session (slave);
+        queue_greeter_reset (slave);
 }
 
 static void
@@ -813,8 +902,9 @@ on_greeter_connected (GdmGreeterServer *
         gboolean display_is_local;
 
         g_debug ("GdmSimpleSlave: Greeter connected");
-
-        gdm_session_open (GDM_SESSION (slave->priv->session));
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
 
         g_object_get (slave,
                       "display-is-local", &display_is_local,
@@ -828,21 +918,29 @@ on_greeter_connected (GdmGreeterServer *
 
 static void
 on_start_session_when_ready (GdmGreeterServer *session,
+                             const char       *service_name,
                              GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: Will start session when ready");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         slave->priv->start_session_when_ready = TRUE;
 
         if (slave->priv->waiting_to_start_session) {
-                gdm_simple_slave_accredit_when_ready (slave);
+                gdm_simple_slave_accredit_when_ready (slave, service_name);
         }
 }
 
 static void
 on_start_session_later (GdmGreeterServer *session,
+                        const char       *service_name,
                         GdmSimpleSlave   *slave)
 {
         g_debug ("GdmSimpleSlave: Will start session when ready and told");
+        if (slave->priv->greeter_reset_id > 0) {
+                return;
+        }
         slave->priv->start_session_when_ready = FALSE;
 }
 
@@ -850,6 +948,15 @@ static void
 setup_server (GdmSimpleSlave *slave)
 {
         /* Set the busy cursor */
+
+        /* The root window has a background that may be useful
+         * to cross fade or transition from when setting the
+         * login screen background.  We read it here, and stuff
+         * it into the standard _XROOTPMAP_ID root window property,
+         * so gnome-settings-daemon can get at it.
+         */
+        gdm_slave_save_root_windows (GDM_SLAVE (slave));
+
         gdm_slave_set_busy_cursor (GDM_SLAVE (slave));
 
         /* The root window has a background that may be useful
@@ -908,6 +1015,10 @@ start_greeter (GdmSimpleSlave *slave)
 
         slave->priv->greeter_server = gdm_greeter_server_new (display_id);
         g_signal_connect (slave->priv->greeter_server,
+                          "start-conversation",
+                          G_CALLBACK (on_greeter_start_conversation),
+                          slave);
+        g_signal_connect (slave->priv->greeter_server,
                           "begin-auto-login",
                           G_CALLBACK (on_greeter_begin_auto_login),
                           slave);
@@ -1017,8 +1128,17 @@ idle_connect_to_display (GdmSimpleSlave 
                 if (! enabled || delay > 0) {
                         start_greeter (slave);
                         create_new_session (slave);
+
+                        if (enabled) {
+                                g_debug ("GdmSimpleSlave: timed login enabled, starting autologin pam conversation\n");
+                                gdm_session_start_conversation (GDM_SESSION (slave->priv->session),
+                                                                "gdm-autologin");
+                        }
                 } else {
                         reset_session (slave);
+                        g_debug ("GdmSimpleSlave: autologin enabled, starting autologin pam conversation\n");
+                        gdm_session_start_conversation (GDM_SESSION (slave->priv->session),
+                                                        "gdm-autologin");
                 }
         } else {
                 if (slave->priv->connection_attempts >= MAX_CONNECT_ATTEMPTS) {
diff -up gdm-2.25.2/daemon/test-session.c.multistack-but-boring gdm-2.25.2/daemon/test-session.c
--- gdm-2.25.2/daemon/test-session.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/daemon/test-session.c	2009-03-04 21:03:53.130431720 -0500
@@ -33,20 +33,22 @@
 static GMainLoop *loop;
 
 static void
-on_open (GdmSession *session,
-         const char *username)
+on_conversation_started (GdmSession *session,
+                         const char *service_name,
+                         const char *username)
 {
         g_debug ("Got opened: calling setup...");
 
-        gdm_session_setup (session, "gdm");
+        gdm_session_setup (session, service_name);
 }
 
 static void
 on_session_setup_complete (GdmSession *session,
+                           const char *service_name,
                            gpointer    data)
 {
         g_debug ("Session setup complete");
-        gdm_session_authenticate (session);
+        gdm_session_authenticate (session, service_name);
 }
 
 static void
@@ -78,10 +80,11 @@ on_session_reset_failed (GdmSession *ses
 
 static void
 on_session_authenticated (GdmSession *session,
+                          const char *service_name,
                           gpointer    data)
 {
         g_debug ("Session authenticated");
-        gdm_session_authorize (session);
+        gdm_session_authorize (session, service_name);
 }
 
 static void
@@ -96,14 +99,16 @@ on_session_authentication_failed (GdmSes
 
 static void
 on_session_authorized (GdmSession *session,
+                       const char *service_name,
                        gpointer    data)
 {
         g_debug ("Session authorized");
-        gdm_session_accredit (session, GDM_SESSION_CRED_ESTABLISH);
+        gdm_session_accredit (session, service_name, GDM_SESSION_CRED_ESTABLISH);
 }
 
 static void
 on_session_authorization_failed (GdmSession *session,
+                                 const char *service_name,
                                  const char *message,
                                  gpointer    data)
 {
@@ -114,6 +119,7 @@ on_session_authorization_failed (GdmSess
 
 static void
 on_session_accredited (GdmSession *session,
+                       const char *service_name,
                        gpointer    data)
 {
         char *username;
@@ -124,12 +130,13 @@ on_session_accredited (GdmSession *sessi
                  username ? username : "", username ? " " : "");
         g_free (username);
 
-        gdm_session_start_session (session);
+        gdm_session_start_session (session, service_name);
 
 }
 
 static void
 on_session_accreditation_failed (GdmSession *session,
+                                 const char *service_name,
                                  const char *message,
                                  gpointer    data)
 {
@@ -164,6 +171,7 @@ on_session_died (GdmSession *session,
 
 static void
 on_info_query (GdmSession *session,
+               const char *service_name,
                const char *query_text)
 {
         char answer[1024];
@@ -177,12 +185,13 @@ on_info_query (GdmSession *session,
                 gdm_session_close (session);
                 g_main_loop_quit (loop);
         } else {
-                gdm_session_answer_query (session, answer);
+                gdm_session_answer_query (session, service_name, answer);
         }
 }
 
 static void
 on_info (GdmSession *session,
+         const char *service_name,
          const char *info)
 {
         g_print ("\n** NOTE: %s\n", info);
@@ -190,6 +199,7 @@ on_info (GdmSession *session,
 
 static void
 on_problem (GdmSession *session,
+            const char *service_name,
             const char *problem)
 {
         g_print ("\n** WARNING: %s\n", problem);
@@ -197,6 +207,7 @@ on_problem (GdmSession *session,
 
 static void
 on_secret_info_query (GdmSession *session,
+                      const char *service_name,
                       const char *query_text)
 {
         char           answer[1024];
@@ -221,7 +232,7 @@ on_secret_info_query (GdmSession *sessio
 
         g_print ("\n");
 
-        gdm_session_answer_query (session, answer);
+        gdm_session_answer_query (session, service_name, answer);
 }
 
 static void
@@ -256,11 +267,11 @@ main (int   argc,
                         username = argv[1];
                 }
 
-                gdm_session_open (GDM_SESSION (session));
+                gdm_session_start_conversation (GDM_SESSION (session), "gdm");
 
                 g_signal_connect (session,
-                                  "opened",
-                                  G_CALLBACK (on_open),
+                                  "conversation-started",
+                                  G_CALLBACK (on_conversation_started),
                                   username);
                 g_signal_connect (session,
                                   "setup-complete",
diff -up gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.c.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.c
--- gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.c.multistack-but-boring	2008-09-08 20:53:24.000000000 -0400
+++ gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.c	2009-03-04 21:03:53.133449134 -0500
@@ -93,6 +93,7 @@ struct GdmChooserWidgetPrivate
         guint32                  should_hide_inactive_items : 1;
         guint32                  emit_activated_after_resize_animation : 1;
         guint32                  was_fully_grown : 1;
+        guint32                  is_loaded : 1;
 
         GdmChooserWidgetPosition separator_position;
         GdmChooserWidgetState    state;
@@ -2132,13 +2133,30 @@ gdm_chooser_widget_lookup_item (GdmChoos
         }
         g_free (active_item_id);
 
-        gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
-                            CHOOSER_IMAGE_COLUMN, image,
-                            CHOOSER_NAME_COLUMN, name,
-                            CHOOSER_PRIORITY_COLUMN, priority,
-                            CHOOSER_ITEM_IS_IN_USE_COLUMN, is_in_use,
-                            CHOOSER_ITEM_IS_SEPARATED_COLUMN, is_separate,
-                            -1);
+        if (image != NULL) {
+                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
+                                    CHOOSER_IMAGE_COLUMN, image, -1);
+        }
+
+        if (name != NULL) {
+                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
+                                    CHOOSER_NAME_COLUMN, name, -1);
+        }
+
+        if (priority != NULL) {
+                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
+                                    CHOOSER_PRIORITY_COLUMN, priority, -1);
+        }
+
+        if (is_in_use != NULL) {
+                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
+                                    CHOOSER_ITEM_IS_IN_USE_COLUMN, is_in_use, -1);
+        }
+
+        if (is_separate != NULL) {
+                gtk_tree_model_get (GTK_TREE_MODEL (widget->priv->list_store), &iter,
+                                    CHOOSER_ITEM_IS_SEPARATED_COLUMN, is_separate, -1);
+        }
 
         return TRUE;
 }
@@ -2485,8 +2503,16 @@ gdm_chooser_widget_propagate_pending_key
         gdm_scrollable_widget_replay_queued_key_events (GDM_SCROLLABLE_WIDGET (widget->priv->scrollable_widget));
 }
 
+gboolean
+gdm_chooser_widget_is_loaded (GdmChooserWidget *widget)
+{
+        return widget->priv->is_loaded;
+}
+
 void
 gdm_chooser_widget_loaded (GdmChooserWidget *widget)
 {
+        widget->priv->is_loaded = TRUE;
+
         g_signal_emit (widget, signals[LOADED], 0);
 }
diff -up gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.h.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.h
--- gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.h.multistack-but-boring	2008-09-08 20:36:20.000000000 -0400
+++ gdm-2.25.2/gui/simple-greeter/gdm-chooser-widget.h	2009-03-04 21:03:53.134447581 -0500
@@ -136,6 +136,10 @@ int            gdm_chooser_widget_get_nu
 void           gdm_chooser_widget_activate_if_one_item         (GdmChooserWidget          *widget);
 void           gdm_chooser_widget_propagate_pending_key_events (GdmChooserWidget          *widget);
 
+gboolean       gdm_chooser_widget_is_loaded                    (GdmChooserWidget          *widget);
+
+/* Protected
+ */
 void           gdm_chooser_widget_loaded                       (GdmChooserWidget          *widget);
 
 G_END_DECLS
diff -up gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.c.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.c
--- gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.c	2009-03-04 21:03:53.136447620 -0500
@@ -64,6 +64,7 @@ enum {
         INFO_QUERY,
         SECRET_INFO_QUERY,
         READY,
+        CONVERSATION_STOPPED,
         RESET,
         SELECTED_USER_CHANGED,
         DEFAULT_LANGUAGE_NAME_CHANGED,
@@ -134,6 +135,37 @@ emit_string_and_int_signal_for_message (
 }
 
 static void
+emit_string_and_string_signal_for_message (GdmGreeterClient *client,
+                                           const char       *name,
+                                           DBusMessage      *message,
+                                           int               signal)
+{
+        DBusError   error;
+        char *text1;
+        char *text2;
+        dbus_bool_t res;
+
+        dbus_error_init (&error);
+        res = dbus_message_get_args (message,
+                                     &error,
+                                     DBUS_TYPE_STRING, &text1,
+                                     DBUS_TYPE_STRING, &text2,
+                                     DBUS_TYPE_INVALID);
+        if (res) {
+
+                g_debug ("GdmGreeterClient: Received %s (%s, %s)", name, text1, text2);
+
+                g_signal_emit (client,
+                               gdm_greeter_client_signals[signal],
+                               0, text1, text2);
+        } else {
+                g_warning ("Unable to get arguments: %s", error.message);
+                dbus_error_free (&error);
+        }
+        dbus_error_free (&error);
+}
+
+static void
 emit_string_signal_for_message (GdmGreeterClient *client,
                                 const char       *name,
                                 DBusMessage      *message,
@@ -200,48 +232,49 @@ static void
 on_user_authorized (GdmGreeterClient *client,
                     DBusMessage      *message)
 {
-        g_signal_emit (client,
-                       gdm_greeter_client_signals[USER_AUTHORIZED],
-                       0);
+        emit_string_signal_for_message (client, "UserAuthorized", message, USER_AUTHORIZED);
 }
 
 static void
 on_info_query (GdmGreeterClient *client,
                DBusMessage      *message)
 {
-        emit_string_signal_for_message (client, "InfoQuery", message, INFO_QUERY);
+        emit_string_and_string_signal_for_message (client, "InfoQuery", message, INFO_QUERY);
 }
 
 static void
 on_secret_info_query (GdmGreeterClient *client,
                       DBusMessage      *message)
 {
-        emit_string_signal_for_message (client, "SecretInfoQuery", message, SECRET_INFO_QUERY);
+        emit_string_and_string_signal_for_message (client, "SecretInfoQuery", message, SECRET_INFO_QUERY);
 }
 
 static void
 on_info (GdmGreeterClient *client,
          DBusMessage      *message)
 {
-        emit_string_signal_for_message (client, "Info", message, INFO);
+        emit_string_and_string_signal_for_message (client, "Info", message, INFO);
 }
 
 static void
 on_problem (GdmGreeterClient *client,
             DBusMessage      *message)
 {
-        emit_string_signal_for_message (client, "Problem", message, PROBLEM);
+        emit_string_and_string_signal_for_message (client, "Problem", message, PROBLEM);
 }
 
 static void
 on_ready (GdmGreeterClient *client,
           DBusMessage      *message)
 {
-        g_debug ("GdmGreeterClient: Ready");
+        emit_string_signal_for_message (client, "Ready", message, READY);
+}
 
-        g_signal_emit (client,
-                       gdm_greeter_client_signals[READY],
-                       0);
+static void
+on_conversation_stopped (GdmGreeterClient *client,
+                         DBusMessage      *message)
+{
+        emit_string_signal_for_message (client, "ConversationStopped", message, CONVERSATION_STOPPED);
 }
 
 static void
@@ -311,14 +344,22 @@ send_dbus_string_method (DBusConnection 
 }
 
 static gboolean
-send_dbus_bool_method (DBusConnection *connection,
-                       const char     *method,
-                       gboolean        payload)
+send_dbus_string_and_bool_method (DBusConnection *connection,
+                                  const char     *method,
+                                  const char     *string_payload,
+                                  gboolean        bool_payload)
 {
         DBusError       error;
         DBusMessage    *message;
         DBusMessage    *reply;
         DBusMessageIter iter;
+        const char     *str;
+
+        if (string_payload != NULL) {
+                str = string_payload;
+        } else {
+                str = "";
+        }
 
         g_debug ("GdmGreeterClient: Calling %s", method);
         message = dbus_message_new_method_call (NULL,
@@ -332,8 +373,77 @@ send_dbus_bool_method (DBusConnection *c
 
         dbus_message_iter_init_append (message, &iter);
         dbus_message_iter_append_basic (&iter,
+                                        DBUS_TYPE_STRING,
+                                        &str);
+
+        dbus_message_iter_append_basic (&iter,
                                         DBUS_TYPE_BOOLEAN,
-                                        &payload);
+                                        &bool_payload);
+
+        dbus_error_init (&error);
+        reply = dbus_connection_send_with_reply_and_block (connection,
+                                                           message,
+                                                           -1,
+                                                           &error);
+
+        dbus_message_unref (message);
+
+        if (dbus_error_is_set (&error)) {
+                g_warning ("%s %s raised: %s\n",
+                           method,
+                           error.name,
+                           error.message);
+                return FALSE;
+        }
+        if (reply != NULL) {
+                dbus_message_unref (reply);
+        }
+        dbus_connection_flush (connection);
+
+        return TRUE;
+}
+
+static gboolean
+send_dbus_string_and_string_method (DBusConnection *connection,
+                                    const char     *method,
+                                    const char     *payload1,
+                                    const char     *payload2)
+{
+        DBusError       error;
+        DBusMessage    *message;
+        DBusMessage    *reply;
+        DBusMessageIter iter;
+        const char     *str;
+
+        g_debug ("GdmGreeterClient: Calling %s", method);
+        message = dbus_message_new_method_call (NULL,
+                                                GREETER_SERVER_DBUS_PATH,
+                                                GREETER_SERVER_DBUS_INTERFACE,
+                                                method);
+        if (message == NULL) {
+                g_warning ("Couldn't allocate the D-Bus message");
+                return FALSE;
+        }
+
+        dbus_message_iter_init_append (message, &iter);
+
+        if (payload1 != NULL) {
+                str = payload1;
+        } else {
+                str = "";
+        }
+        dbus_message_iter_append_basic (&iter,
+                                        DBUS_TYPE_STRING,
+                                        &str);
+
+        if (payload2 != NULL) {
+                str = payload2;
+        } else {
+                str = "";
+        }
+        dbus_message_iter_append_basic (&iter,
+                                        DBUS_TYPE_STRING,
+                                        &str);
 
         dbus_error_init (&error);
         reply = dbus_connection_send_with_reply_and_block (connection,
@@ -400,6 +510,14 @@ send_dbus_void_method (DBusConnection *c
 }
 
 void
+gdm_greeter_client_call_start_conversation (GdmGreeterClient *client,
+                                            const char       *service_name)
+{
+        send_dbus_string_method (client->priv->connection,
+                                 "StartConversation", service_name);
+}
+
+void
 gdm_greeter_client_call_begin_auto_login (GdmGreeterClient *client,
                                           const char       *username)
 {
@@ -408,37 +526,44 @@ gdm_greeter_client_call_begin_auto_login
 }
 
 void
-gdm_greeter_client_call_begin_verification (GdmGreeterClient *client)
+gdm_greeter_client_call_begin_verification (GdmGreeterClient *client,
+                                            const char       *service_name)
 {
-        send_dbus_void_method (client->priv->connection,
-                               "BeginVerification");
+        send_dbus_string_method (client->priv->connection,
+                                 "BeginVerification", service_name);
 }
 
 void
 gdm_greeter_client_call_begin_verification_for_user (GdmGreeterClient *client,
+                                                     const char       *service_name,
                                                      const char       *username)
 {
-        send_dbus_string_method (client->priv->connection,
-                                 "BeginVerificationForUser",
-                                 username);
+        send_dbus_string_and_string_method (client->priv->connection,
+                                            "BeginVerificationForUser",
+                                            service_name,
+                                            username);
 }
 
 void
 gdm_greeter_client_call_answer_query (GdmGreeterClient *client,
+                                      const char       *service_name,
                                       const char       *text)
 {
-        send_dbus_string_method (client->priv->connection,
-                                 "AnswerQuery",
-                                 text);
+        send_dbus_string_and_string_method (client->priv->connection,
+                                            "AnswerQuery",
+                                            service_name,
+                                            text);
 }
 
 void
 gdm_greeter_client_call_start_session_when_ready  (GdmGreeterClient *client,
+                                                   const char       *service_name,
                                                    gboolean          should_start_session)
 {
-        send_dbus_bool_method (client->priv->connection,
-                               "StartSessionWhenReady",
-                               should_start_session);
+        send_dbus_string_and_bool_method (client->priv->connection,
+                                          "StartSessionWhenReady",
+                                          service_name,
+                                          should_start_session);
 }
 
 void
@@ -643,6 +768,8 @@ client_dbus_handle_message (DBusConnecti
                 on_problem (client, message);
         } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "Ready")) {
                 on_ready (client, message);
+        } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "ConversationStopped")) {
+                on_conversation_stopped (client, message);
         } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "Reset")) {
                 on_reset (client, message);
         } else if (dbus_message_is_signal (message, GREETER_SERVER_DBUS_INTERFACE, "SelectedUserChanged")) {
@@ -831,10 +958,10 @@ gdm_greeter_client_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterClientClass, info_query),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
 
         gdm_greeter_client_signals[SECRET_INFO_QUERY] =
                 g_signal_new ("secret-info-query",
@@ -843,10 +970,10 @@ gdm_greeter_client_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterClientClass, secret_info_query),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
 
         gdm_greeter_client_signals[INFO] =
                 g_signal_new ("info",
@@ -855,10 +982,10 @@ gdm_greeter_client_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterClientClass, info),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
 
         gdm_greeter_client_signals[PROBLEM] =
                 g_signal_new ("problem",
@@ -867,10 +994,10 @@ gdm_greeter_client_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterClientClass, problem),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1,
-                              G_TYPE_STRING);
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
 
         gdm_greeter_client_signals[READY] =
                 g_signal_new ("ready",
@@ -879,9 +1006,20 @@ gdm_greeter_client_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterClientClass, ready),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
+
+        gdm_greeter_client_signals[CONVERSATION_STOPPED] =
+                g_signal_new ("conversation-stopped",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterClientClass, conversation_stopped),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1, G_TYPE_STRING);
 
         gdm_greeter_client_signals[RESET] =
                 g_signal_new ("reset",
@@ -952,8 +1090,9 @@ gdm_greeter_client_class_init (GdmGreete
                               G_STRUCT_OFFSET (GdmGreeterClientClass, user_authorized),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
-                              G_TYPE_NONE, 0);
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1, G_TYPE_STRING);
 }
 
 static void
diff -up gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.h.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.h
--- gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.h.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-client.h	2009-03-04 21:03:53.137433986 -0500
@@ -45,17 +45,24 @@ typedef struct
         GObjectClass   parent_class;
 
         void (* info_query)              (GdmGreeterClient  *client,
+                                          const char        *service_name,
                                           const char        *query_text);
 
         void (* secret_info_query)       (GdmGreeterClient  *client,
+                                          const char        *service_name,
                                           const char        *query_text);
 
         void (* info)                    (GdmGreeterClient  *client,
+                                          const char        *service_name,
                                           const char        *info);
 
         void (* problem)                 (GdmGreeterClient  *client,
+                                          const char        *service_name,
                                           const char        *problem);
-        void (* ready)                   (GdmGreeterClient  *client);
+        void (* ready)                   (GdmGreeterClient  *client,
+                                          const char        *service_name);
+        void (* conversation_stopped)    (GdmGreeterClient  *client,
+                                          const char        *service_name);
         void (* reset)                   (GdmGreeterClient  *client);
         void (* selected_user_changed)   (GdmGreeterClient  *client,
                                           const char        *username);
@@ -69,7 +76,8 @@ typedef struct
         void (* timed_login_requested)   (GdmGreeterClient  *client,
                                           const char        *username,
                                           int                delay);
-        void (* user_authorized)         (GdmGreeterClient  *client);
+        void (* user_authorized)         (GdmGreeterClient  *client,
+                                          const char        *service_name);
 } GdmGreeterClientClass;
 
 #define GDM_GREETER_CLIENT_ERROR (gdm_greeter_client_error_quark ())
@@ -84,17 +92,21 @@ GQuark             gdm_greeter_client_er
 GdmGreeterClient * gdm_greeter_client_new                            (void);
 
 gboolean           gdm_greeter_client_start                          (GdmGreeterClient *client,
-                                                                         GError          **error);
+                                                                      GError          **error);
 void               gdm_greeter_client_stop                           (GdmGreeterClient *client);
 
 gboolean           gdm_greeter_client_get_display_is_local           (GdmGreeterClient *client);
 
 char *             gdm_greeter_client_call_get_display_id            (GdmGreeterClient *client);
 
+void               gdm_greeter_client_call_start_conversation        (GdmGreeterClient *client,
+                                                                      const char       *service_name);
 void               gdm_greeter_client_call_begin_auto_login          (GdmGreeterClient *client,
                                                                       const char       *username);
-void               gdm_greeter_client_call_begin_verification        (GdmGreeterClient *client);
+void               gdm_greeter_client_call_begin_verification        (GdmGreeterClient *client,
+                                                                      const char       *service_name);
 void               gdm_greeter_client_call_begin_verification_for_user (GdmGreeterClient *client,
+                                                                        const char       *service_name,
                                                                         const char       *username);
 void               gdm_greeter_client_call_cancel                    (GdmGreeterClient *client);
 void               gdm_greeter_client_call_disconnect                (GdmGreeterClient *client);
@@ -109,9 +121,11 @@ void               gdm_greeter_client_ca
 void               gdm_greeter_client_call_select_session            (GdmGreeterClient *client,
                                                                       const char       *text);
 void               gdm_greeter_client_call_answer_query              (GdmGreeterClient *client,
+                                                                      const char       *service_name,
                                                                       const char       *text);
 
 void               gdm_greeter_client_call_start_session_when_ready  (GdmGreeterClient *client,
+                                                                      const char       *service_name,
                                                                       gboolean          should_start_session);
 
 
diff -up gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.c.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.c
--- gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.c.multistack-but-boring	2008-11-18 17:19:05.000000000 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.c	2009-03-05 09:33:10.435596361 -0500
@@ -1,7 +1,7 @@
 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
  *
  * Copyright (C) 2007 William Jon McCann <mccann@jhu.edu>
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008, 2009 Red Hat, Inc.
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -17,6 +17,9 @@
  * along with this program; if not, write to the Free Software
  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  *
+ * Written by: William Jon McCann <mccann@jhu.edu>
+ *             Ray Strode <rstrode@redhat.com>
+ *
  */
 
 #include "config.h"
@@ -60,12 +63,16 @@
 #include <polkit-gnome/polkit-gnome.h>
 #endif
 
+#include "gdm-marshal.h"
+
 #include "gdm-settings-client.h"
 #include "gdm-settings-keys.h"
 #include "gdm-profile.h"
 
+#include "gdm-greeter-client.h"
 #include "gdm-greeter-login-window.h"
 #include "gdm-user-chooser-widget.h"
+#include "gdm-task-list.h"
 
 #ifdef HAVE_PAM
 #include <security/pam_appl.h>
@@ -111,7 +118,9 @@ struct GdmGreeterLoginWindowPrivate
 {
         GladeXML        *xml;
         GtkWidget       *user_chooser;
+        GtkWidget       *conversation_list;
         GtkWidget       *auth_banner_label;
+        GtkWidget       *auth_page_box;
         guint            display_is_local : 1;
         guint            is_interactive : 1;
         GConfClient     *client;
@@ -139,6 +148,7 @@ enum {
 };
 
 enum {
+        START_CONVERSATION,
         BEGIN_AUTO_LOGIN,
         BEGIN_VERIFICATION,
         BEGIN_VERIFICATION_FOR_USER,
@@ -160,6 +170,9 @@ static void     restart_timed_login_time
 static void     on_user_unchosen            (GdmUserChooserWidget *user_chooser,
                                              GdmGreeterLoginWindow *login_window);
 
+static void     gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_window,
+                                                                   const char            *service_name);
+
 G_DEFINE_TYPE (GdmGreeterLoginWindow, gdm_greeter_login_window, GTK_TYPE_WINDOW)
 
 static void
@@ -184,9 +197,6 @@ set_sensitive (GdmGreeterLoginWindow *lo
 {
         GtkWidget *box;
 
-        box = glade_xml_get_widget (login_window->priv->xml, "auth-input-box");
-        gtk_widget_set_sensitive (box, sensitive);
-
         box = glade_xml_get_widget (login_window->priv->xml, "buttonbox");
         gtk_widget_set_sensitive (box, sensitive);
 
@@ -196,27 +206,43 @@ set_sensitive (GdmGreeterLoginWindow *lo
 static void
 set_focus (GdmGreeterLoginWindow *login_window)
 {
-        GtkWidget *entry;
-
-        entry = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry");
+        GdmTask *task;
 
         gdk_window_focus (GTK_WIDGET (login_window)->window, GDK_CURRENT_TIME);
 
-        if (GTK_WIDGET_REALIZED (entry) && ! GTK_WIDGET_HAS_FOCUS (entry)) {
-                gtk_widget_grab_focus (entry);
+        task = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
+
+        if (gdm_conversation_focus (GDM_CONVERSATION (task))) {
+                char *name;
+                name = gdm_task_get_name (task);
+                g_debug ("GdmGreeterLoginWindow: focusing task %s", name);
+                g_free (name);
         } else if (GTK_WIDGET_REALIZED (login_window->priv->user_chooser) && ! GTK_WIDGET_HAS_FOCUS (login_window->priv->user_chooser)) {
                 gtk_widget_grab_focus (login_window->priv->user_chooser);
         }
+        g_object_unref (task);
+}
+
+static gboolean
+set_task_conversation_message (GdmTaskList *task_list,
+                               GdmTask     *task,
+                               const char  *message)
+{
+
+        gdm_conversation_set_message (GDM_CONVERSATION (task), message);
+        return FALSE;
 }
 
 static void
 set_message (GdmGreeterLoginWindow *login_window,
              const char            *text)
 {
-        GtkWidget *label;
+        g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window));
 
-        label = glade_xml_get_widget (login_window->priv->xml, "auth-message-label");
-        gtk_label_set_text (GTK_LABEL (label), text);
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc)
+                                    set_task_conversation_message,
+                                    (gpointer) text);
 }
 
 static void
@@ -382,30 +408,76 @@ get_show_restart_buttons (GdmGreeterLogi
 }
 
 static void
-on_login_button_clicked_answer_query (GtkButton             *button,
-                                      GdmGreeterLoginWindow *login_window)
+on_login_button_clicked_timed_login (GtkButton             *button,
+                                     GdmGreeterLoginWindow *login_window)
 {
-        GtkWidget  *entry;
-        const char *text;
-
         set_busy (login_window);
         set_sensitive (login_window, FALSE);
 
-        entry = glade_xml_get_widget (login_window->priv->xml, "auth-prompt-entry");
-        text = gtk_entry_get_text (GTK_ENTRY (entry));
-
         _gdm_greeter_login_window_set_interactive (login_window, TRUE);
-        g_signal_emit (login_window, signals[QUERY_ANSWER], 0, text);
 }
 
 static void
-on_login_button_clicked_timed_login (GtkButton             *button,
-                                     GdmGreeterLoginWindow *login_window)
+hide_task_actions (GdmTask *task)
 {
-        set_busy (login_window);
-        set_sensitive (login_window, FALSE);
+        GtkActionGroup *actions;
 
-        _gdm_greeter_login_window_set_interactive (login_window, TRUE);
+        actions = gdm_conversation_get_actions (GDM_CONVERSATION (task));
+
+        if (actions != NULL) {
+                gtk_action_group_set_visible (actions, FALSE);
+                gtk_action_group_set_sensitive (actions, FALSE);
+                g_object_unref (actions);
+        }
+}
+
+static void
+grab_default_button_for_task (GdmTask *task)
+{
+        GtkActionGroup *actions;
+        GtkAction *action;
+        GSList    *proxies, *node;
+
+        actions = gdm_conversation_get_actions (GDM_CONVERSATION (task));
+
+        if (actions == NULL) {
+                return;
+        }
+
+        action = gtk_action_group_get_action (actions, GDM_CONVERSATION_DEFAULT_ACTION);
+        g_object_unref (actions);
+
+        if (action == NULL) {
+                return;
+        }
+
+        proxies = gtk_action_get_proxies (action);
+        for (node = proxies; node != NULL; node = node->next) {
+                GtkWidget *widget;
+
+                widget = GTK_WIDGET (node->data);
+
+                if (GTK_WIDGET_CAN_DEFAULT (widget) &&
+                    GTK_WIDGET_VISIBLE (widget)) {
+                        gtk_widget_grab_default (widget);
+                        break;
+                }
+        }
+
+}
+
+static void
+show_task_actions (GdmTask *task)
+{
+        GtkActionGroup *actions;
+
+        actions = gdm_conversation_get_actions (GDM_CONVERSATION (task));
+
+        if (actions != NULL) {
+                gtk_action_group_set_sensitive (actions, TRUE);
+                gtk_action_group_set_visible (actions, TRUE);
+                g_object_unref (actions);
+        }
 }
 
 static void
@@ -413,6 +485,7 @@ set_log_in_button_mode (GdmGreeterLoginW
                         int                    mode)
 {
         GtkWidget *button;
+        GdmTask   *task;
 
         button = glade_xml_get_widget (login_window->priv->xml, "log-in-button");
         gtk_widget_grab_default (button);
@@ -425,14 +498,27 @@ set_log_in_button_mode (GdmGreeterLoginW
 
         switch (mode) {
         case LOGIN_BUTTON_HIDDEN:
+                task = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
+                if (task != NULL) {
+                        hide_task_actions (task);
+                        g_object_unref (task);
+                }
+
                 gtk_widget_hide (button);
                 break;
         case LOGIN_BUTTON_ANSWER_QUERY:
-                login_window->priv->login_button_handler_id = g_signal_connect (button, "clicked", G_CALLBACK (on_login_button_clicked_answer_query), login_window);
-                gtk_widget_show (button);
+                task = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
+                if (task != NULL) {
+                        show_task_actions (task);
+                        grab_default_button_for_task (task);
+                        g_object_unref (task);
+                }
+
+                gtk_widget_hide (button);
                 break;
         case LOGIN_BUTTON_TIMED_LOGIN:
                 login_window->priv->login_button_handler_id = g_signal_connect (button, "clicked", G_CALLBACK (on_login_button_clicked_timed_login), login_window);
+
                 gtk_widget_show (button);
                 break;
         default:
@@ -528,6 +614,7 @@ switch_mode (GdmGreeterLoginWindow *logi
         GtkWidget  *box;
         gboolean    show_restart_buttons;
         gboolean    show_suspend_button;
+        int         number_of_tasks;
 
         show_restart_buttons = get_show_restart_buttons (login_window);
         show_suspend_button = can_suspend (login_window);
@@ -557,7 +644,8 @@ switch_mode (GdmGreeterLoginWindow *logi
                 show_widget (login_window, "disconnect-button",
                              ! login_window->priv->display_is_local);
 
-                show_widget (login_window, "auth-input-box", FALSE);
+                show_widget (login_window, "auth-page-box", FALSE);
+                show_widget (login_window, "conversation-list", FALSE);
 
                 add_sensitize_power_buttons_timeout (login_window);
                 sensitize_widget (login_window, "shutdown-button", FALSE);
@@ -576,6 +664,11 @@ switch_mode (GdmGreeterLoginWindow *logi
                 show_widget (login_window, "restart-button", FALSE);
                 show_widget (login_window, "suspend-button", FALSE);
                 show_widget (login_window, "disconnect-button", FALSE);
+                show_widget (login_window, "auth-page-box", TRUE);
+
+                number_of_tasks = gdm_task_list_get_number_of_tasks (GDM_TASK_LIST (login_window->priv->conversation_list));
+                show_widget (login_window, "conversation-list", number_of_tasks > 1);
+
                 default_name = "log-in-button";
                 break;
         default:
@@ -655,25 +748,54 @@ do_suspend (GdmGreeterLoginWindow *login
         g_object_unref (proxy);
 }
 
-static void
-delete_entry_text (GtkWidget *entry)
+static gboolean
+task_has_service_name (GdmTaskList *task_list,
+                       GdmTask     *task,
+                       const char  *service_name)
+{
+        char *task_service_name;
+        gboolean has_service_name;
+
+        task_service_name = gdm_conversation_get_service_name (GDM_CONVERSATION (task));
+
+        has_service_name = strcmp (service_name, task_service_name) == 0;
+        g_free (task_service_name);
+
+        return has_service_name;
+}
+
+GdmTask *
+find_task_with_service_name (GdmGreeterLoginWindow *login_window,
+                             const char            *service_name)
 {
-        const char *typed_text;
-        char       *null_text;
+        GdmTask *task;
+
+        task = gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                           (GdmTaskListForeachFunc)
+                                           task_has_service_name,
+                                           (gpointer) service_name);
 
-        /* try to scrub out any secret info */
-        typed_text = gtk_entry_get_text (GTK_ENTRY (entry));
-        null_text = g_strnfill (strlen (typed_text) + 1, '\b');
-        gtk_entry_set_text (GTK_ENTRY (entry), null_text);
-        gtk_entry_set_text (GTK_ENTRY (entry), "");
+        return task;
+}
+
+static gboolean
+reset_task (GdmTaskList           *task_list,
+            GdmTask               *task,
+            GdmGreeterLoginWindow *login_window)
+{
+        char *name;
+
+        name = gdm_task_get_name (task);
+        g_debug ("Resetting task '%s'", name);
+        g_free (name);
+
+        gdm_conversation_reset (GDM_CONVERSATION (task));
+        return FALSE;
 }
 
 static void
 reset_dialog (GdmGreeterLoginWindow *login_window)
 {
-        GtkWidget  *entry;
-        GtkWidget  *label;
-
         g_debug ("GdmGreeterLoginWindow: Resetting dialog");
         set_busy (login_window);
         set_sensitive (login_window, FALSE);
@@ -697,18 +819,15 @@ reset_dialog (GdmGreeterLoginWindow *log
                 login_window->priv->start_session_handler_id = 0;
         }
 
-        entry = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry");
-
-        delete_entry_text (entry);
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc)
+                                    reset_task,
+                                    login_window);
 
-        gtk_entry_set_visibility (GTK_ENTRY (entry), TRUE);
         set_message (login_window, "");
-
-        label = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-label");
-        gtk_label_set_text (GTK_LABEL (label), "");
-
         switch_mode (login_window, MODE_SELECTION);
 
+        gtk_widget_set_sensitive (login_window->priv->conversation_list, TRUE);
         set_sensitive (login_window, TRUE);
         set_ready (login_window);
         set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
@@ -716,21 +835,61 @@ reset_dialog (GdmGreeterLoginWindow *log
 }
 
 static void
-do_cancel (GdmGreeterLoginWindow *login_window)
+restart_conversations (GdmGreeterLoginWindow *login_window)
 {
-        /* need to wait for response from backend */
-        set_message (login_window, _("Cancelling..."));
         set_busy (login_window);
         set_sensitive (login_window, FALSE);
         g_signal_emit (login_window, signals[CANCELLED], 0);
 }
 
+static void
+do_cancel (GdmGreeterLoginWindow *login_window)
+{
+        /* need to wait for response from backend */
+        set_message (login_window, _("Cancelling..."));
+        restart_conversations (login_window);
+}
+
+static void
+on_can_set_task_ready (GtkWidget *user_chooser,
+                       GdmTask   *task)
+{
+        g_signal_handlers_disconnect_by_func (user_chooser,
+                                              on_can_set_task_ready,
+                                              task);
+        gdm_conversation_set_ready (GDM_CONVERSATION (task));
+        g_object_unref (task);
+}
+
+static void
+set_task_ready_when_loaded (GdmGreeterLoginWindow *login_window,
+                            GdmTask               *task)
+{
+        g_signal_connect (login_window->priv->user_chooser,
+                          "loaded",
+                          G_CALLBACK (on_can_set_task_ready),
+                          g_object_ref (task));
+}
+
 gboolean
-gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_ready (GdmGreeterLoginWindow *login_window,
+                                const char            *service_name)
 {
+        GdmTask *task;
+
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
-        reset_dialog (login_window);
+        task = find_task_with_service_name (login_window, service_name);
+
+        if (task != NULL) {
+                if (gdm_chooser_widget_is_loaded (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser))) {
+                        gdm_conversation_set_ready (GDM_CONVERSATION (task));
+                } else {
+
+                        set_task_ready_when_loaded (login_window, task);
+                }
+                g_object_unref (task);
+        }
 
         set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE);
         set_ready (GDM_GREETER_LOGIN_WINDOW (login_window));
@@ -740,37 +899,112 @@ gdm_greeter_login_window_ready (GdmGreet
 }
 
 gboolean
-gdm_greeter_login_window_reset (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_conversation_stopped (GdmGreeterLoginWindow *login_window,
+                                               const char            *service_name)
 {
+        GdmTask *task;
+
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
+        g_debug ("GdmGreeterLoginWindow: conversation '%s' has stopped", service_name);
+
+        task = find_task_with_service_name (login_window, service_name);
+
+        if (task != NULL) {
+                gdm_conversation_reset (GDM_CONVERSATION (task));
+                g_object_unref (task);
+        }
+
+        /* If every conversation has failed, then just start over.
+         */
+        task = gdm_task_list_get_active_task (GDM_TASK_LIST (login_window->priv->conversation_list));
+
+        if (!gdm_task_is_enabled (task)) {
+                g_debug ("GdmGreeterLoginWindow: No conversations left, starting over");
+                restart_conversations (login_window);
+        }
+        g_object_unref (task);
+
+        return TRUE;
+}
+
+static gboolean
+restart_task_conversation (GdmTaskList           *task_list,
+                           GdmTask               *task,
+                           GdmGreeterLoginWindow *login_window)
+{
+        char *service_name;
+
+        service_name = gdm_conversation_get_service_name (GDM_CONVERSATION (task));
+        if (service_name != NULL) {
+                char *name;
+
+                name = gdm_task_get_name (task);
+                g_debug ("GdmGreeterLoginWindow: restarting '%s' conversation", name);
+                g_free (name);
+
+                g_signal_emit (login_window, signals[START_CONVERSATION], 0, service_name);
+                g_free (service_name);
+        }
+
+        return FALSE;
+}
+
+gboolean
+gdm_greeter_login_window_reset (GdmGreeterLoginWindow *login_window)
+{
+        g_debug ("GdmGreeterLoginWindow: window reset");
+
+        g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
         reset_dialog (GDM_GREETER_LOGIN_WINDOW (login_window));
 
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc)
+                                    restart_task_conversation,
+                                    login_window);
+
         return TRUE;
 }
 
 gboolean
 gdm_greeter_login_window_info (GdmGreeterLoginWindow *login_window,
+                               const char            *service_name,
                                const char            *text)
 {
-        g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
+        GdmTask *task;
 
+        g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
         g_debug ("GdmGreeterLoginWindow: info: %s", text);
 
-        set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        task = find_task_with_service_name (login_window, service_name);
+
+        if (task != NULL) {
+                gdm_conversation_set_message (GDM_CONVERSATION (task),
+                                              text);
+                g_object_unref (task);
+        }
 
         return TRUE;
 }
 
 gboolean
 gdm_greeter_login_window_problem (GdmGreeterLoginWindow *login_window,
+                                  const char            *service_name,
                                   const char            *text)
 {
-        g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
+        GdmTask *task;
 
+        g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
         g_debug ("GdmGreeterLoginWindow: problem: %s", text);
 
-        set_message (GDM_GREETER_LOGIN_WINDOW (login_window), text);
+        task = find_task_with_service_name (login_window, service_name);
+
+        if (task != NULL) {
+                gdm_conversation_set_message (GDM_CONVERSATION (task),
+                                              text);
+                g_object_unref (task);
+        }
+
         gdk_window_beep (GTK_WIDGET (login_window)->window);
 
         return TRUE;
@@ -808,11 +1042,21 @@ gdm_greeter_login_window_request_timed_l
 }
 
 static void
-gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_window)
+on_ready_to_start_session (GdmGreeterLoginWindow *login_window,
+                           GParamSpec            *param_spec,
+                           char                  *service_name)
+{
+        gdm_greeter_login_window_start_session_when_ready (login_window, service_name);
+        g_free (service_name);
+}
+
+static void
+gdm_greeter_login_window_start_session_when_ready (GdmGreeterLoginWindow *login_window,
+                                                   const char            *service_name)
 {
         if (login_window->priv->is_interactive) {
                 g_debug ("GdmGreeterLoginWindow: starting session");
-                g_signal_emit (login_window, signals[START_SESSION], 0);
+                g_signal_emit (login_window, signals[START_SESSION], 0, service_name);
         } else {
                 g_debug ("GdmGreeterLoginWindow: not starting session since "
                          "user hasn't had an opportunity to pick language "
@@ -822,8 +1066,8 @@ gdm_greeter_login_window_start_session_w
                  */
                 login_window->priv->start_session_handler_id =
                     g_signal_connect (login_window, "notify::is-interactive",
-                                      G_CALLBACK (gdm_greeter_login_window_start_session_when_ready),
-                                      NULL);
+                                      G_CALLBACK (on_ready_to_start_session),
+                                      g_strdup (service_name));
 
                 /* FIXME: If the user wasn't asked any questions by pam but
                  * pam still authorized them (passwd -d, or the questions got
@@ -846,24 +1090,25 @@ gdm_greeter_login_window_start_session_w
 
 gboolean
 gdm_greeter_login_window_info_query (GdmGreeterLoginWindow *login_window,
+                                     const char            *service_name,
                                      const char            *text)
 {
-        GtkWidget  *entry;
-        GtkWidget  *label;
+        GdmTask *task;
 
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
         g_debug ("GdmGreeterLoginWindow: info query: %s", text);
 
-        entry = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry");
-        delete_entry_text (entry);
-        gtk_entry_set_visibility (GTK_ENTRY (entry), TRUE);
-        set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
 
-        label = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-label");
-        gtk_label_set_text (GTK_LABEL (label), text);
+        task = find_task_with_service_name (login_window, service_name);
+
+        if (task != NULL) {
+                gdm_conversation_ask_question (GDM_CONVERSATION (task),
+                                               text);
+                g_object_unref (task);
+        }
 
-        show_widget (login_window, "auth-input-box", TRUE);
+        set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
         set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE);
         set_ready (GDM_GREETER_LOGIN_WINDOW (login_window));
         set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
@@ -875,22 +1120,23 @@ gdm_greeter_login_window_info_query (Gdm
 
 gboolean
 gdm_greeter_login_window_secret_info_query (GdmGreeterLoginWindow *login_window,
+                                            const char            *service_name,
                                             const char            *text)
 {
-        GtkWidget  *entry;
-        GtkWidget  *label;
+
+        GdmTask *task;
 
         g_return_val_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window), FALSE);
 
-        entry = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-entry");
-        delete_entry_text (entry);
-        gtk_entry_set_visibility (GTK_ENTRY (entry), FALSE);
-        set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
+        task = find_task_with_service_name (login_window, service_name);
 
-        label = glade_xml_get_widget (GDM_GREETER_LOGIN_WINDOW (login_window)->priv->xml, "auth-prompt-label");
-        gtk_label_set_text (GTK_LABEL (label), text);
+        if (task != NULL) {
+                gdm_conversation_ask_secret (GDM_CONVERSATION (task),
+                                             text);
+                g_object_unref (task);
+        }
 
-        show_widget (login_window, "auth-input-box", TRUE);
+        set_log_in_button_mode (login_window, LOGIN_BUTTON_ANSWER_QUERY);
         set_sensitive (GDM_GREETER_LOGIN_WINDOW (login_window), TRUE);
         set_ready (GDM_GREETER_LOGIN_WINDOW (login_window));
         set_focus (GDM_GREETER_LOGIN_WINDOW (login_window));
@@ -901,13 +1147,16 @@ gdm_greeter_login_window_secret_info_que
 }
 
 void
-gdm_greeter_login_window_user_authorized (GdmGreeterLoginWindow *login_window)
+gdm_greeter_login_window_user_authorized (GdmGreeterLoginWindow *login_window,
+                                          const char            *service_name)
 {
         g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window));
 
-        g_debug ("GdmGreeterLoginWindow: user now authorized");
+        g_debug ("GdmGreeterLoginWindow: user now authorized via service %s",
+                  service_name);
 
-        gdm_greeter_login_window_start_session_when_ready (login_window);
+        gdm_greeter_login_window_start_session_when_ready (login_window,
+                                                           service_name);
 }
 
 static void
@@ -1330,43 +1579,183 @@ on_users_loaded (GdmUserChooserWidget  *
         gdm_chooser_widget_activate_if_one_item (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser));
 }
 
+static gboolean
+begin_task_verification (GdmTaskList           *task_list,
+                         GdmTask               *task,
+                         GdmGreeterLoginWindow *login_window)
+{
+        char *service_name;
+
+        service_name = gdm_conversation_get_service_name (GDM_CONVERSATION (task));
+        if (service_name != NULL) {
+                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0, service_name);
+                g_free (service_name);
+        }
+
+        return FALSE;
+}
+
 static void
-on_user_chosen (GdmUserChooserWidget  *user_chooser,
-                GdmGreeterLoginWindow *login_window)
+begin_verification (GdmGreeterLoginWindow *login_window)
+{
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc)
+                                    begin_task_verification,
+                                    login_window);
+
+        switch_mode (login_window, MODE_AUTHENTICATION);
+}
+
+static gboolean
+begin_task_verification_for_selected_user (GdmTaskList           *task_list,
+                                           GdmTask               *task,
+                                           GdmGreeterLoginWindow *login_window)
 {
         char *user_name;
+        char *service_name;
 
         user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser));
-        g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
 
         if (user_name == NULL) {
-                return;
+                return TRUE;
+        }
+
+        service_name = gdm_conversation_get_service_name (GDM_CONVERSATION (task));
+        if (service_name != NULL) {
+                g_signal_emit (login_window, signals[BEGIN_VERIFICATION_FOR_USER], 0, service_name, user_name);
+                g_free (service_name);
         }
 
+        g_free (user_name);
+        return FALSE;
+}
+
+static void
+begin_verification_for_selected_user (GdmGreeterLoginWindow *login_window)
+{
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc)
+                                    begin_task_verification_for_selected_user,
+                                    login_window);
+}
+
+static void
+on_user_chosen (GdmGreeterLoginWindow *login_window,
+                const char            *user_name)
+{
+        g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
+
         g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
                        0, user_name);
 
-        if (strcmp (user_name, GDM_USER_CHOOSER_USER_OTHER) == 0) {
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0);
-        } else if (strcmp (user_name, GDM_USER_CHOOSER_USER_GUEST) == 0) {
-                /* FIXME: handle guest account stuff */
-        } else if (strcmp (user_name, GDM_USER_CHOOSER_USER_AUTO) == 0) {
-                g_signal_emit (login_window, signals[BEGIN_AUTO_LOGIN], 0,
-                               login_window->priv->timed_login_username);
-
-                login_window->priv->timed_login_enabled = TRUE;
-                restart_timed_login_timeout (login_window);
-
-                /* just wait for the user to select language and stuff */
-                set_log_in_button_mode (login_window, LOGIN_BUTTON_TIMED_LOGIN);
-                set_message (login_window, _("Select language and click Log In"));
-        } else {
-                g_signal_emit (login_window, signals[BEGIN_VERIFICATION_FOR_USER], 0, user_name);
+        begin_verification_for_selected_user (login_window);
+
+        switch_mode (login_window, MODE_AUTHENTICATION);
+}
+
+static void
+begin_auto_login (GdmGreeterLoginWindow *login_window)
+{
+        g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
+                       0, GDM_USER_CHOOSER_USER_AUTO);
+
+        g_signal_emit (login_window, signals[BEGIN_AUTO_LOGIN], 0,
+                       login_window->priv->timed_login_username);
+
+        login_window->priv->timed_login_enabled = TRUE;
+        restart_timed_login_timeout (login_window);
+
+        /* just wait for the user to select language and stuff */
+        set_log_in_button_mode (login_window, LOGIN_BUTTON_TIMED_LOGIN);
+        set_message (login_window, _("Select language and click Log In"));
+
+        switch_mode (login_window, MODE_AUTHENTICATION);
+}
+
+static gboolean
+reset_task_if_not_given (GdmTaskList *task_list,
+                         GdmTask     *task,
+                         GdmTask     *given_task)
+{
+        if (task == given_task) {
+                return FALSE;
         }
 
+        gdm_conversation_reset (GDM_CONVERSATION (task));
+        return FALSE;
+}
+
+static void
+reset_every_task_but_given_task (GdmGreeterLoginWindow *login_window,
+                                 GdmTask               *task)
+{
+        gdm_task_list_foreach_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                    (GdmTaskListForeachFunc)
+                                    reset_task_if_not_given,
+                                    task);
+}
+
+static void
+begin_single_service_verification (GdmGreeterLoginWindow *login_window,
+                                   const char            *service_name)
+{
+        GdmTask *task;
+
+        task = find_task_with_service_name (login_window, service_name);
+
+        if (task == NULL) {
+                g_debug ("GdmGreeterLoginWindow: %s has no task associated with it", service_name);
+                return;
+        }
+        g_debug ("GdmGreeterLoginWindow: Beginning %s auth conversation", service_name);
+
+        /* FIXME: we should probably give the plugin more say for
+         * what happens here.
+         */
+        g_signal_emit (login_window, signals[BEGIN_VERIFICATION], 0, service_name);
+
         switch_mode (login_window, MODE_AUTHENTICATION);
+        gdm_task_list_set_active_task (GDM_TASK_LIST (login_window->priv->conversation_list), task);
 
-        g_free (user_name);
+        reset_every_task_but_given_task (login_window, task);
+
+        g_object_unref (task);
+}
+
+static void
+on_user_chooser_activated (GdmUserChooserWidget  *user_chooser,
+                           GdmGreeterLoginWindow *login_window)
+{
+        char *user_name;
+        char *item_id;
+
+        user_name = gdm_user_chooser_widget_get_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser));
+
+        if (user_name != NULL) {
+                g_debug ("GdmGreeterLoginWindow: user chosen '%s'", user_name);
+                on_user_chosen (login_window, user_name);
+                g_free (user_name);
+                return;
+        }
+
+        item_id = gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (user_chooser));
+        g_debug ("GdmGreeterLoginWindow: item chosen '%s'", item_id);
+
+        if (strcmp (item_id, GDM_USER_CHOOSER_USER_OTHER) == 0) {
+                g_debug ("GdmGreeterLoginWindow: Starting all auth conversations");
+                g_free (item_id);
+
+                begin_verification (login_window);
+        } else if (strcmp (item_id, GDM_USER_CHOOSER_USER_AUTO) == 0) {
+                g_debug ("GdmGreeterLoginWindow: Starting auto login");
+                g_free (item_id);
+
+                begin_auto_login (login_window);
+        } else {
+
+                begin_single_service_verification (login_window, item_id);
+                g_free (item_id);
+        }
 }
 
 static void
@@ -1505,6 +1894,69 @@ create_computer_info (GdmGreeterLoginWin
 #define INVISIBLE_CHAR_BULLET        0x2022
 #define INVISIBLE_CHAR_NONE          0
 
+static void
+on_task_activated (GdmGreeterLoginWindow *login_window,
+                   GdmTask               *task)
+{
+        GtkWidget *container;
+        char *name;
+
+        name = gdm_task_get_name (task);
+        g_debug ("GdmGreeterLoginWindow: task '%s' activated", name);
+        g_free (name);
+
+        container = g_object_get_data (G_OBJECT (task),
+                                       "gdm-greeter-login-window-page-container");
+
+        if (container == NULL) {
+                GtkWidget *page;
+
+                container = gtk_alignment_new (0.5, 0.5, 1.0, 1.0);
+                gtk_container_add (GTK_CONTAINER (login_window->priv->auth_page_box),
+                                   container);
+
+                page = gdm_conversation_get_page (GDM_CONVERSATION (task));
+                if (page != NULL) {
+                        gtk_container_add (GTK_CONTAINER (container), page);
+                        gtk_widget_show (page);
+                }
+                g_object_set_data (G_OBJECT (task),
+                                   "gdm-greeter-login-window-page-container",
+                                   container);
+        }
+
+        gtk_widget_show (container);
+        set_log_in_button_mode (login_window, login_window->priv->dialog_mode);
+}
+
+static void
+on_task_deactivated (GdmGreeterLoginWindow *login_window,
+                     GdmTask               *task)
+{
+        GtkWidget *container;
+        char *name;
+        GtkActionGroup *actions;
+
+        name = gdm_task_get_name (task);
+        g_debug ("GdmGreeterLoginWindow: task '%s' now in background", name);
+        g_free (name);
+
+        container = g_object_get_data (G_OBJECT (task),
+                                       "gdm-greeter-login-window-page-container");
+
+        if (container != NULL) {
+                gtk_widget_hide (container);
+        }
+
+        actions = gdm_conversation_get_actions (GDM_CONVERSATION (task));
+
+        if (actions != NULL) {
+                gtk_action_group_set_sensitive (actions, FALSE);
+                gtk_action_group_set_visible (actions, FALSE);
+                g_object_unref (actions);
+        }
+}
+
 static GtkWidget *
 custom_widget_constructor (GladeXML              *xml,
                            char                  *func_name,
@@ -1527,6 +1979,8 @@ custom_widget_constructor (GladeXML     
 
         if (strcmp (name, "user-chooser") == 0) {
                widget = gdm_user_chooser_widget_new ();
+        } else if (strcmp (name, "conversation-list") == 0) {
+               widget = gdm_task_list_new ();
         }
 
         gdm_profile_end (NULL);
@@ -1537,7 +1991,6 @@ custom_widget_constructor (GladeXML     
 static void
 load_theme (GdmGreeterLoginWindow *login_window)
 {
-        GtkWidget *entry;
         GtkWidget *button;
         GtkWidget *box;
         GtkWidget *image;
@@ -1591,7 +2044,7 @@ load_theme (GdmGreeterLoginWindow *login
                           login_window);
         g_signal_connect (login_window->priv->user_chooser,
                           "activated",
-                          G_CALLBACK (on_user_chosen),
+                          G_CALLBACK (on_user_chooser_activated),
                           login_window);
         g_signal_connect (login_window->priv->user_chooser,
                           "deactivated",
@@ -1600,8 +2053,20 @@ load_theme (GdmGreeterLoginWindow *login
 
         gtk_widget_show (login_window->priv->user_chooser);
 
+        login_window->priv->conversation_list = glade_xml_get_widget (login_window->priv->xml,
+                                                                      "conversation-list");
+        g_signal_connect_swapped (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                  "activated",
+                                  G_CALLBACK (on_task_activated),
+                                  login_window);
+        g_signal_connect_swapped (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                  "deactivated",
+                                  G_CALLBACK (on_task_deactivated),
+                                  login_window);
+
         login_window->priv->auth_banner_label = glade_xml_get_widget (login_window->priv->xml, "auth-banner-label");
         /*make_label_small_italic (login_window->priv->auth_banner_label);*/
+        login_window->priv->auth_page_box = glade_xml_get_widget (login_window->priv->xml, "auth-page-box");
 
         button = glade_xml_get_widget (login_window->priv->xml, "suspend-button");
         g_signal_connect (button, "clicked", G_CALLBACK (suspend_button_clicked), login_window);
@@ -1617,14 +2082,6 @@ load_theme (GdmGreeterLoginWindow *login
         button = glade_xml_get_widget (login_window->priv->xml, "shutdown-button");
         g_signal_connect (button, "clicked", G_CALLBACK (shutdown_button_clicked), login_window);
 
-        entry = glade_xml_get_widget (login_window->priv->xml, "auth-prompt-entry");
-        /* Only change the invisible character if it '*' otherwise assume it is OK */
-        if ('*' == gtk_entry_get_invisible_char (GTK_ENTRY (entry))) {
-                gunichar invisible_char;
-                invisible_char = INVISIBLE_CHAR_BLACK_CIRCLE;
-                gtk_entry_set_invisible_char (GTK_ENTRY (entry), invisible_char);
-        }
-
         create_computer_info (login_window);
 
         box = glade_xml_get_widget (login_window->priv->xml, "computer-info-event-box");
@@ -1757,6 +2214,15 @@ gdm_greeter_login_window_class_init (Gdm
         widget_class->key_press_event = gdm_greeter_login_window_key_press_event;
         widget_class->size_request = gdm_greeter_login_window_size_request;
 
+        signals [START_CONVERSATION] =
+                g_signal_new ("start-conversation",
+                              G_TYPE_FROM_CLASS (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, start_conversation),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE, 1, G_TYPE_STRING);
         signals [BEGIN_AUTO_LOGIN] =
                 g_signal_new ("begin-auto-login",
                               G_TYPE_FROM_CLASS (object_class),
@@ -1773,9 +2239,9 @@ gdm_greeter_login_window_class_init (Gdm
                               G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, begin_verification),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
         signals [BEGIN_VERIFICATION_FOR_USER] =
                 g_signal_new ("begin-verification-for-user",
                               G_TYPE_FROM_CLASS (object_class),
@@ -1783,9 +2249,9 @@ gdm_greeter_login_window_class_init (Gdm
                               G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, begin_verification_for_user),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1, G_TYPE_STRING);
+                              2, G_TYPE_STRING, G_TYPE_STRING);
         signals [QUERY_ANSWER] =
                 g_signal_new ("query-answer",
                               G_TYPE_FROM_CLASS (object_class),
@@ -1793,9 +2259,9 @@ gdm_greeter_login_window_class_init (Gdm
                               G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, query_answer),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__STRING,
+                              gdm_marshal_VOID__STRING_STRING,
                               G_TYPE_NONE,
-                              1, G_TYPE_STRING);
+                              2, G_TYPE_STRING, G_TYPE_STRING);
         signals [USER_SELECTED] =
                 g_signal_new ("user-selected",
                               G_TYPE_FROM_CLASS (object_class),
@@ -1833,9 +2299,9 @@ gdm_greeter_login_window_class_init (Gdm
                               G_STRUCT_OFFSET (GdmGreeterLoginWindowClass, start_session),
                               NULL,
                               NULL,
-                              g_cclosure_marshal_VOID__VOID,
+                              g_cclosure_marshal_VOID__STRING,
                               G_TYPE_NONE,
-                              0);
+                              1, G_TYPE_STRING);
 
         g_object_class_install_property (object_class,
                                          PROP_DISPLAY_IS_LOCAL,
@@ -1888,6 +2354,232 @@ on_gconf_key_changed (GConfClient       
         }
 }
 
+static void
+on_conversation_answer (GdmGreeterLoginWindow *login_window,
+                        const char            *text,
+                        GdmConversation       *conversation)
+{
+        if (text != NULL) {
+                char *service_name;
+
+                service_name = gdm_conversation_get_service_name (conversation);
+                if (service_name != NULL) {
+                        g_signal_emit (login_window, signals[QUERY_ANSWER], 0, service_name, text);
+                        g_free (service_name);
+                }
+        }
+
+        set_sensitive (login_window, TRUE);
+        set_ready (login_window);
+}
+
+static void
+on_conversation_cancel (GdmGreeterLoginWindow *login_window,
+                        GdmConversation       *conversation)
+{
+        do_cancel (login_window);
+}
+
+static gboolean
+on_conversation_chose_user (GdmGreeterLoginWindow *login_window,
+                            const char            *username,
+                            GdmConversation       *conversation)
+{
+        if (!gdm_chooser_widget_is_loaded (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser))) {
+                char *name;
+
+                name = gdm_task_get_name (GDM_TASK (conversation));
+                g_warning ("Task %s is trying to choose user before list is loaded", name);
+                g_free (name);
+                return FALSE;
+        }
+
+        /* If we're already authenticating then we can't pick a user
+         */
+        if (login_window->priv->dialog_mode == MODE_AUTHENTICATION) {
+                return FALSE;
+        }
+
+        if (gdm_task_list_set_active_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                           GDM_TASK (conversation))) {
+                gdm_user_chooser_widget_set_chosen_user_name (GDM_USER_CHOOSER_WIDGET (login_window->priv->user_chooser),
+                                                              username);
+        }
+
+        return TRUE;
+}
+
+void
+gdm_greeter_login_window_remove_extension (GdmGreeterLoginWindow *login_window,
+ GdmGreeterExtension *extension)
+{
+        g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window));
+        g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW_EXTENSION (extension));
+
+        if (!GDM_IS_CONVERSATION (extension)) {
+                return;
+        }
+}
+
+static void
+on_button_action_label_changed (GtkWidget *button)
+{
+        GtkAction *action;
+        char *text;
+
+        action = gtk_widget_get_action (button);
+
+        g_object_get (G_OBJECT (action), "label", &text, NULL);
+
+        gtk_button_set_label (GTK_BUTTON (button), text);
+        g_free (text);
+}
+
+static void
+on_button_action_icon_name_changed (GtkWidget *button)
+{
+        GtkAction *action;
+        GtkWidget *image;
+
+        action = gtk_widget_get_action (button);
+
+        image = gtk_action_create_icon (GTK_ACTION (action), GTK_ICON_SIZE_BUTTON);
+        gtk_button_set_image (GTK_BUTTON (button), image);
+}
+
+static void
+on_button_action_tooltip_changed (GtkWidget *button)
+{
+        GtkAction *action;
+        char *text;
+
+        action = gtk_widget_get_action (button);
+
+        g_object_get (G_OBJECT (action), "tooltip", &text, NULL);
+
+        gtk_widget_set_tooltip_text (button, text);
+        g_free (text);
+}
+
+GtkWidget *
+create_button_from_action (GtkAction *action)
+{
+        GtkWidget *button;
+
+        button = gtk_button_new ();
+
+        gtk_action_connect_proxy (GTK_ACTION (action), button);
+
+        g_signal_connect_swapped (action,
+                                  "notify::label",
+                                  G_CALLBACK (on_button_action_label_changed),
+                                  button);
+        g_signal_connect_swapped (action,
+                                  "notify::icon-name",
+                                  G_CALLBACK (on_button_action_icon_name_changed),
+                                  button);
+        g_signal_connect_swapped (action,
+                                  "notify::tooltip",
+                                  G_CALLBACK (on_button_action_tooltip_changed),
+                                  button);
+
+        on_button_action_label_changed (button);
+        on_button_action_icon_name_changed (button);
+        on_button_action_tooltip_changed (button);
+
+        if (strcmp (gtk_action_get_name (action),
+                    GDM_CONVERSATION_DEFAULT_ACTION) == 0) {
+                GTK_WIDGET_SET_FLAGS (button, GTK_CAN_DEFAULT);
+        }
+
+        return button;
+}
+
+static void
+create_buttons_for_actions (GdmGreeterLoginWindow *login_window,
+                            GtkActionGroup        *actions)
+{
+        GList *action_list;
+        GList *node;
+        GtkWidget *box;
+
+        action_list = gtk_action_group_list_actions (actions);
+
+        box = glade_xml_get_widget (login_window->priv->xml, "buttonbox");
+        for (node = action_list; node != NULL; node = node->next) {
+                GtkAction *action;
+                GtkWidget *button;
+
+                action = node->data;
+
+                button = create_button_from_action (action);
+                gtk_container_add (GTK_CONTAINER (box), button);
+        }
+
+        g_list_free (action_list);
+}
+
+void
+gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
+                                        GdmGreeterExtension *extension)
+{
+        char *name;
+        char *description;
+        char *service_name;
+        GtkActionGroup *actions;
+
+        g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW (login_window));
+        g_return_if_fail (GDM_IS_GREETER_LOGIN_WINDOW_EXTENSION (extension));
+
+        if (!GDM_IS_CONVERSATION (extension)) {
+                return;
+        }
+
+        actions = gdm_conversation_get_actions (GDM_CONVERSATION (extension));
+
+        create_buttons_for_actions (login_window, actions);
+        hide_task_actions (GDM_TASK (extension));
+
+        g_object_unref (actions);
+
+        g_signal_connect_swapped (GDM_CONVERSATION (extension),
+                                  "answer",
+                                  G_CALLBACK (on_conversation_answer),
+                                  login_window);
+        g_signal_connect_swapped (GDM_CONVERSATION (extension),
+                                  "cancel",
+                                  G_CALLBACK (on_conversation_cancel),
+                                  login_window);
+        g_signal_connect_swapped (GDM_CONVERSATION (extension),
+                                  "user-chosen",
+                                  G_CALLBACK (on_conversation_chose_user),
+                                  login_window);
+
+        name = gdm_task_get_name (GDM_TASK (extension));
+        description = gdm_task_get_description (GDM_TASK (extension));
+
+        g_debug ("GdmGreeterLoginWindow: new extension '%s - %s' added",
+                name, description);
+
+        gdm_task_list_add_task (GDM_TASK_LIST (login_window->priv->conversation_list),
+                                GDM_TASK (extension));
+
+        service_name = gdm_conversation_get_service_name (GDM_CONVERSATION (extension));
+
+        if (gdm_task_is_choosable (GDM_TASK (extension))) {
+                gdm_chooser_widget_add_item (GDM_CHOOSER_WIDGET (login_window->priv->user_chooser),
+                                             service_name, NULL, name, description, ~0,
+                                             FALSE, TRUE);
+        }
+
+        g_free (name);
+        g_free (description);
+
+        g_debug ("GdmGreeterLoginWindow: starting conversation with '%s'", service_name);
+        g_signal_emit (login_window, signals[START_CONVERSATION], 0, service_name);
+        g_free (service_name);
+}
+
 static gboolean
 on_window_state_event (GtkWidget           *widget,
                        GdkEventWindowState *event,
diff -up gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.glade.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.glade
--- gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.glade.multistack-but-boring	2008-11-18 17:34:37.000000000 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.glade	2009-03-04 21:03:53.143431937 -0500
@@ -611,30 +611,29 @@
 	      </child>
 
 	      <child>
-		<widget class="GtkLabel" id="auth-banner-label">
-		  <property name="visible">True</property>
-		  <property name="label" translatable="yes"></property>
-		  <property name="use_underline">False</property>
-		  <property name="use_markup">False</property>
-		  <property name="justify">GTK_JUSTIFY_CENTER</property>
-		  <property name="wrap">True</property>
-		  <property name="selectable">False</property>
-		  <property name="xalign">0.5</property>
-		  <property name="yalign">0.5</property>
-		  <property name="xpad">0</property>
-		  <property name="ypad">0</property>
-		  <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-		  <property name="width_chars">-1</property>
-		  <property name="single_line_mode">False</property>
-		  <property name="angle">0</property>
-		</widget>
-		<packing>
-		  <property name="padding">0</property>
-		  <property name="expand">True</property>
-		  <property name="fill">True</property>
-		</packing>
+		 <widget class="GtkLabel" id="auth-banner-label">
+		   <property name="visible">True</property>
+		   <property name="label" translatable="yes"></property>
+		   <property name="use_underline">False</property>
+		   <property name="use_markup">False</property>
+		   <property name="justify">GTK_JUSTIFY_CENTER</property>
+		   <property name="wrap">True</property>
+		   <property name="selectable">False</property>
+		   <property name="xalign">0.5</property>
+		   <property name="yalign">0.5</property>
+		   <property name="xpad">0</property>
+		   <property name="ypad">0</property>
+		   <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
+		   <property name="width_chars">-1</property>
+		   <property name="single_line_mode">False</property>
+		   <property name="angle">0</property>
+		 </widget>
+		 <packing>
+		   <property name="padding">0</property>
+		   <property name="expand">False</property>
+		   <property name="fill">False</property>
+		 </packing>
 	      </child>
-
 	      <child>
 		<widget class="GtkAlignment" id="alignment2">
 		  <property name="visible">True</property>
@@ -654,42 +653,17 @@
 		      <property name="spacing">10</property>
 
 		      <child>
-			<widget class="Custom" id="user-chooser">
-			  <property name="visible">True</property>
-			  <property name="int1">0</property>
-			  <property name="int2">0</property>
-			  <property name="last_modification_time">Tue, 18 Nov 2008 21:55:38 GMT</property>
-			</widget>
-			<packing>
-			  <property name="padding">0</property>
-			  <property name="expand">True</property>
-			  <property name="fill">True</property>
-			</packing>
-		      </child>
-
-		      <child>
-			<widget class="GtkHBox" id="auth-input-box">
+			<widget class="GtkVBox" id="vbox6">
 			  <property name="visible">True</property>
 			  <property name="homogeneous">False</property>
-			  <property name="spacing">6</property>
+			  <property name="spacing">0</property>
 
 			  <child>
-			    <widget class="GtkLabel" id="auth-prompt-label">
+			    <widget class="Custom" id="conversation-list">
 			      <property name="visible">True</property>
-			      <property name="label" translatable="yes"></property>
-			      <property name="use_underline">False</property>
-			      <property name="use_markup">False</property>
-			      <property name="justify">GTK_JUSTIFY_LEFT</property>
-			      <property name="wrap">False</property>
-			      <property name="selectable">False</property>
-			      <property name="xalign">0.5</property>
-			      <property name="yalign">0.5</property>
-			      <property name="xpad">0</property>
-			      <property name="ypad">0</property>
-			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-			      <property name="width_chars">-1</property>
-			      <property name="single_line_mode">False</property>
-			      <property name="angle">0</property>
+			      <property name="int1">0</property>
+			      <property name="int2">0</property>
+			      <property name="last_modification_time">Fri, 30 Jan 2009 16:03:30 GMT</property>
 			    </widget>
 			    <packing>
 			      <property name="padding">0</property>
@@ -699,16 +673,11 @@
 			  </child>
 
 			  <child>
-			    <widget class="GtkEntry" id="auth-prompt-entry">
+			    <widget class="Custom" id="user-chooser">
 			      <property name="visible">True</property>
-			      <property name="can_focus">True</property>
-			      <property name="events">GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK</property>
-			      <property name="editable">True</property>
-			      <property name="visibility">True</property>
-			      <property name="max_length">0</property>
-			      <property name="text" translatable="yes"></property>
-			      <property name="has_frame">True</property>
-			      <property name="activates_default">True</property>
+			      <property name="int1">0</property>
+			      <property name="int2">0</property>
+			      <property name="last_modification_time">Tue, 18 Nov 2008 21:55:38 GMT</property>
 			    </widget>
 			    <packing>
 			      <property name="padding">0</property>
@@ -718,46 +686,21 @@
 			    </packing>
 			  </child>
 
-			  <child>
-			    <placeholder/>
-			  </child>
 			</widget>
 			<packing>
 			  <property name="padding">0</property>
-			  <property name="expand">False</property>
-			  <property name="fill">False</property>
+			  <property name="expand">True</property>
+			  <property name="fill">True</property>
 			</packing>
 		      </child>
 
 		      <child>
-			<widget class="GtkHBox" id="auth-message-box">
+			<widget class="GtkHBox" id="auth-page-box">
 			  <property name="visible">True</property>
 			  <property name="homogeneous">False</property>
 			  <property name="spacing">0</property>
-
 			  <child>
-			    <widget class="GtkLabel" id="auth-message-label">
-			      <property name="visible">True</property>
-			      <property name="label" translatable="yes"></property>
-			      <property name="use_underline">False</property>
-			      <property name="use_markup">False</property>
-			      <property name="justify">GTK_JUSTIFY_LEFT</property>
-			      <property name="wrap">False</property>
-			      <property name="selectable">False</property>
-			      <property name="xalign">0.5</property>
-			      <property name="yalign">0.5</property>
-			      <property name="xpad">0</property>
-			      <property name="ypad">0</property>
-			      <property name="ellipsize">PANGO_ELLIPSIZE_NONE</property>
-			      <property name="width_chars">-1</property>
-			      <property name="single_line_mode">False</property>
-			      <property name="angle">0</property>
-			    </widget>
-			    <packing>
-			      <property name="padding">0</property>
-			      <property name="expand">True</property>
-			      <property name="fill">True</property>
-			    </packing>
+				<placeholder/>
 			  </child>
 			</widget>
 			<packing>
@@ -779,10 +722,6 @@
 	  </child>
 	</widget>
       </child>
-
-      <child>
-	<placeholder/>
-      </child>
     </widget>
   </child>
 </widget>
diff -up gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.h.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.h
--- gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.h.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-login-window.h	2009-03-04 21:03:53.144432201 -0500
@@ -23,6 +23,9 @@
 #define __GDM_GREETER_LOGIN_WINDOW_H
 
 #include <glib-object.h>
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+#include "gdm-greeter-extension.h"
 
 G_BEGIN_DECLS
 
@@ -33,6 +36,8 @@ G_BEGIN_DECLS
 #define GDM_IS_GREETER_LOGIN_WINDOW_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_GREETER_LOGIN_WINDOW))
 #define GDM_GREETER_LOGIN_WINDOW_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_GREETER_LOGIN_WINDOW, GdmGreeterLoginWindowClass))
 
+#define GDM_IS_GREETER_LOGIN_WINDOW_EXTENSION(e) (GDM_IS_CONVERSATION(e) && GDM_IS_TASK(e))
+
 typedef struct GdmGreeterLoginWindowPrivate GdmGreeterLoginWindowPrivate;
 
 typedef struct
@@ -46,18 +51,24 @@ typedef struct
         GtkWindowClass   parent_class;
 
         /* signals */
+        void (* start_conversation)          (GdmGreeterLoginWindow *login_window,
+                                              const char            *service_name);
         void (* begin_auto_login)            (GdmGreeterLoginWindow *login_window,
                                               const char            *username);
-        void (* begin_verification)          (GdmGreeterLoginWindow *login_window);
+        void (* begin_verification)          (GdmGreeterLoginWindow *login_window,
+                                              const char            *service_name);
         void (* begin_verification_for_user) (GdmGreeterLoginWindow *login_window,
+                                              const char            *service_name,
                                               const char            *username);
         void (* query_answer)                (GdmGreeterLoginWindow *login_window,
+                                              const char            *service_name,
                                               const char            *text);
         void (* user_selected)               (GdmGreeterLoginWindow *login_window,
                                               const char            *text);
         void (* cancelled)                   (GdmGreeterLoginWindow *login_window);
         void (* disconnected)                (GdmGreeterLoginWindow *login_window);
-        void (* start_session)               (GdmGreeterLoginWindow *login_window);
+        void (* start_session)               (GdmGreeterLoginWindow *login_window,
+                                              const char            *sevice_name);
 
 } GdmGreeterLoginWindowClass;
 
@@ -66,20 +77,33 @@ GtkWidget *         gdm_greeter_login_wi
 
 
 gboolean            gdm_greeter_login_window_reset              (GdmGreeterLoginWindow *login_window);
-gboolean            gdm_greeter_login_window_ready              (GdmGreeterLoginWindow *login_window);
+gboolean            gdm_greeter_login_window_ready              (GdmGreeterLoginWindow *login_window,
+                                                                 const char            *service_name);
+gboolean            gdm_greeter_login_window_conversation_stopped (GdmGreeterLoginWindow *login_window,
+                                                                   const char            *service_name);
 gboolean            gdm_greeter_login_window_info_query         (GdmGreeterLoginWindow *login_window,
+                                                                 const char *service_name,
                                                                  const char *text);
 gboolean            gdm_greeter_login_window_secret_info_query  (GdmGreeterLoginWindow *login_window,
+                                                                 const char *service_name,
                                                                  const char *text);
 gboolean            gdm_greeter_login_window_info               (GdmGreeterLoginWindow *login_window,
+                                                                 const char *service_name,
                                                                  const char *text);
 gboolean            gdm_greeter_login_window_problem            (GdmGreeterLoginWindow *login_window,
+                                                                 const char *service_name,
                                                                  const char *text);
 
 void               gdm_greeter_login_window_request_timed_login (GdmGreeterLoginWindow *login_window,
                                                                  const char            *username,
                                                                  int                    delay);
-void               gdm_greeter_login_window_user_authorized     (GdmGreeterLoginWindow *login_window);
+void               gdm_greeter_login_window_user_authorized     (GdmGreeterLoginWindow *login_window,
+                                                                 const char            *service_name);
+
+void               gdm_greeter_login_window_add_extension (GdmGreeterLoginWindow *login_window,
+                    GdmGreeterExtension *extension);
+void               gdm_greeter_login_window_remove_extension (GdmGreeterLoginWindow *login_window,
+ GdmGreeterExtension *extension);
 
 G_END_DECLS
 
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/gdm-greeter-plugin.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-plugin.c	2009-03-04 21:03:53.146447953 -0500
@@ -0,0 +1,255 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#include <config.h>
+
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include <glib/gi18n.h>
+#include <gio/gio.h>
+
+#include "gdm-greeter-extension.h"
+#include "gdm-greeter-plugin.h"
+
+#define GDM_GREETER_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_PLUGIN, GdmGreeterPluginPrivate))
+
+enum {
+        PROP_0,
+        PROP_FILENAME,
+};
+
+enum {
+        LOADED,
+        LOAD_FAILED,
+        UNLOADED,
+        LAST_SIGNAL
+};
+
+struct _GdmGreeterPluginPrivate {
+        GObject              parent;
+
+        GModule             *module;
+        char                *filename;
+
+        GdmGreeterExtension *extension;
+};
+
+static void gdm_greeter_plugin_finalize     (GObject      *object);
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
+G_DEFINE_TYPE (GdmGreeterPlugin, gdm_greeter_plugin, G_TYPE_OBJECT)
+
+static void
+gdm_greeter_plugin_set_property (GObject      *object,
+                                 guint         param_id,
+                                 const GValue *value,
+                                 GParamSpec   *pspec)
+{
+        GdmGreeterPlugin *plugin;
+
+        plugin = GDM_GREETER_PLUGIN (object);
+        switch (param_id) {
+        case PROP_FILENAME:
+                plugin->priv->filename = g_strdup (g_value_get_string (value));
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                break;
+        }
+}
+
+static void
+gdm_greeter_plugin_get_property (GObject    *object,
+                                 guint       param_id,
+                                 GValue     *value,
+                                 GParamSpec *pspec)
+{
+        GdmGreeterPlugin *plugin;
+
+        plugin = GDM_GREETER_PLUGIN (object);
+
+        switch (param_id) {
+        case PROP_FILENAME:
+                g_value_set_string (value, plugin->priv->filename);
+                break;
+        default:
+                G_OBJECT_WARN_INVALID_PROPERTY_ID (object, param_id, pspec);
+                break;
+        }
+}
+
+static void
+gdm_greeter_plugin_class_init (GdmGreeterPluginClass *class)
+{
+        GObjectClass *gobject_class;
+
+        gobject_class = G_OBJECT_CLASS (class);
+
+        gobject_class->set_property = gdm_greeter_plugin_set_property;
+        gobject_class->get_property = gdm_greeter_plugin_get_property;
+        gobject_class->finalize = gdm_greeter_plugin_finalize;
+
+        g_object_class_install_property (gobject_class,
+                                         PROP_FILENAME,
+                                         g_param_spec_string ("filename",
+                                                              "Filename",
+                                                              "The full path to the plugin.",
+                                                              NULL,
+                                                              G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+        signals [LOADED] =
+                g_signal_new ("loaded",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterPluginClass, loaded),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [LOAD_FAILED] =
+                g_signal_new ("load-failed",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterPluginClass, load_failed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [UNLOADED] =
+                g_signal_new ("unloaded",
+                              G_TYPE_FROM_CLASS (class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmGreeterPluginClass, unloaded),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        g_type_class_add_private (class, sizeof (GdmGreeterPluginPrivate));
+}
+
+GdmGreeterPlugin *
+gdm_greeter_plugin_new (const char *filename)
+{
+        GObject *object;
+
+        object = g_object_new (GDM_TYPE_GREETER_PLUGIN,
+                               "filename", filename, NULL);
+
+        return GDM_GREETER_PLUGIN (object);
+}
+
+void
+gdm_greeter_plugin_load (GdmGreeterPlugin *plugin)
+{
+        GModule *module;
+        GdmGreeterExtension *extension;
+        union {
+                gpointer symbol;
+                GdmGreeterPluginGetExtensionFunc invoke;
+        } get_extension;
+
+
+        module = g_module_open (plugin->priv->filename, G_MODULE_BIND_LOCAL);
+
+        if (module == NULL) {
+                g_warning ("plugin %s couldn't be opened: %s",
+                           plugin->priv->filename,
+                           g_module_error ());
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+                return;
+        }
+
+        if (!g_module_symbol (module,
+                              "gdm_greeter_plugin_get_extension",
+                              &get_extension.symbol) ||
+            !get_extension.symbol) {
+                g_warning ("plugin %s lacks gdm_greeter_plugin_get_extension()",
+                           plugin->priv->filename);
+                g_module_close (module);
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+                return;
+        }
+
+        extension = get_extension.invoke ();
+
+        if (!extension) {
+                g_warning ("plugin %s didn't return extension when asked",
+                           plugin->priv->filename);
+                g_module_close (module);
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+        }
+
+        if (!GDM_IS_GREETER_EXTENSION (extension)) {
+                g_warning ("plugin %s returned bogus extension when asked",
+                           plugin->priv->filename);
+                g_module_close (module);
+                g_signal_emit (plugin, signals [LOAD_FAILED], 0);
+        }
+
+        plugin->priv->module = module;
+        plugin->priv->extension = extension;
+
+        g_signal_emit (plugin, signals [LOADED], 0);
+}
+
+void
+gdm_greeter_plugin_unload (GdmGreeterPlugin *plugin)
+{
+        if (plugin->priv->extension != NULL) {
+                g_object_unref (plugin->priv->extension);
+                plugin->priv->extension = NULL;
+        }
+
+        if (plugin->priv->module != NULL) {
+                g_module_close (plugin->priv->module);
+                plugin->priv->module = NULL;
+        }
+}
+
+const char *
+gdm_greeter_plugin_get_filename (GdmGreeterPlugin *plugin)
+{
+        return plugin->priv->filename;
+}
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (GdmGreeterPlugin *plugin)
+{
+        return g_object_ref (plugin->priv->extension);
+}
+
+static void
+gdm_greeter_plugin_init (GdmGreeterPlugin *plugin)
+{
+        plugin->priv = GDM_GREETER_PLUGIN_GET_PRIVATE (plugin);
+}
+
+static void
+gdm_greeter_plugin_finalize (GObject *object)
+{
+        GdmGreeterPlugin *plugin;
+
+        plugin = GDM_GREETER_PLUGIN (object);
+
+        gdm_greeter_plugin_unload (plugin);
+}
+
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/gdm-greeter-plugin.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-plugin.h	2009-03-04 21:03:53.147432504 -0500
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef __GDM_GREETER_PLUGIN
+#define __GDM_GREETER_PLUGIN
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_GREETER_PLUGIN (gdm_greeter_plugin_get_type ())
+#define GDM_GREETER_PLUGIN(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), GDM_TYPE_GREETER_PLUGIN, GdmGreeterPlugin))
+#define GDM_IS_GREETER_PLUGIN(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), GDM_TYPE_GREETER_PLUGIN))
+
+typedef struct _GdmGreeterPlugin GdmGreeterPlugin;
+typedef struct _GdmGreeterPluginPrivate GdmGreeterPluginPrivate;
+typedef struct _GdmGreeterPluginClass GdmGreeterPluginClass;
+
+struct _GdmGreeterPlugin
+{
+        GObject                parent;
+        GdmGreeterPluginPrivate *priv;
+};
+
+struct _GdmGreeterPluginClass
+{
+        GObjectClass   parent_class;
+
+        void          (* loaded)         (GdmGreeterPlugin *plugin);
+        void          (* load_failed)    (GdmGreeterPlugin *plugin);
+        void          (* unloaded)       (GdmGreeterPlugin *plugin);
+};
+
+GType             gdm_greeter_plugin_get_type (void) G_GNUC_CONST;
+GdmGreeterPlugin *gdm_greeter_plugin_new (const char *filename);
+void              gdm_greeter_plugin_load (GdmGreeterPlugin *plugin);
+void              gdm_greeter_plugin_unload (GdmGreeterPlugin *plugin);
+const char       *gdm_greeter_plugin_get_filename (GdmGreeterPlugin *plugin);
+GdmGreeterExtension *gdm_greeter_plugin_get_extension (GdmGreeterPlugin *plugin);
+
+G_END_DECLS
+
+#endif
diff -up gdm-2.25.2/gui/simple-greeter/gdm-greeter-session.c.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-greeter-session.c
--- gdm-2.25.2/gui/simple-greeter/gdm-greeter-session.c.multistack-but-boring	2008-08-26 15:04:00.000000000 -0400
+++ gdm-2.25.2/gui/simple-greeter/gdm-greeter-session.c	2009-03-04 21:03:53.149435615 -0500
@@ -38,6 +38,8 @@
 #include "gdm-greeter-panel.h"
 #include "gdm-greeter-login-window.h"
 
+#include "gdm-plugin-manager.h"
+
 #include "gdm-profile.h"
 
 #define GDM_GREETER_SESSION_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_GREETER_SESSION, GdmGreeterSessionPrivate))
@@ -45,9 +47,11 @@
 struct GdmGreeterSessionPrivate
 {
         GdmGreeterClient      *client;
+        GdmPluginManager      *plugin_manager;
 
         GtkWidget             *login_window;
         GtkWidget             *panel;
+
 };
 
 enum {
@@ -64,31 +68,46 @@ static gpointer session_object = NULL;
 
 static void
 on_info (GdmGreeterClient  *client,
+         const char        *service_name,
          const char        *text,
          GdmGreeterSession *session)
 {
         g_debug ("GdmGreeterSession: Info: %s", text);
 
-        gdm_greeter_login_window_info (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+        gdm_greeter_login_window_info (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
 }
 
 static void
 on_problem (GdmGreeterClient  *client,
+            const char        *service_name,
             const char        *text,
             GdmGreeterSession *session)
 {
         g_debug ("GdmGreeterSession: Problem: %s", text);
 
-        gdm_greeter_login_window_problem (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+        gdm_greeter_login_window_problem (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
 }
 
 static void
 on_ready (GdmGreeterClient  *client,
+          const char        *service_name,
           GdmGreeterSession *session)
 {
         g_debug ("GdmGreeterSession: Ready");
 
-        gdm_greeter_login_window_ready (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+        gdm_greeter_login_window_ready (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window),
+                                        service_name);
+}
+
+static void
+on_conversation_stopped (GdmGreeterClient  *client,
+                         const char        *service_name,
+                         GdmGreeterSession *session)
+{
+        g_debug ("GdmGreeterSession: Conversation '%s' stopped", service_name);
+
+        gdm_greeter_login_window_conversation_stopped (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window),
+                                                       service_name);
 }
 
 static void
@@ -151,33 +170,44 @@ on_timed_login_requested (GdmGreeterClie
 
 static void
 on_user_authorized (GdmGreeterClient  *client,
+                    const char        *service_name,
                     GdmGreeterSession *session)
 {
         g_debug ("GdmGreeterSession: user authorized");
-        gdm_greeter_login_window_user_authorized (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window));
+        gdm_greeter_login_window_user_authorized (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name);
 }
 
 static void
 on_info_query (GdmGreeterClient  *client,
+               const char        *service_name,
                const char        *text,
                GdmGreeterSession *session)
 {
         g_debug ("GdmGreeterSession: Info query: %s", text);
 
-        gdm_greeter_login_window_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+        gdm_greeter_login_window_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
 }
 
 static void
 on_secret_info_query (GdmGreeterClient  *client,
+                      const char        *service_name,
                       const char        *text,
                       GdmGreeterSession *session)
 {
         g_debug ("GdmGreeterSession: Secret info query: %s", text);
 
-        gdm_greeter_login_window_secret_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), text);
+        gdm_greeter_login_window_secret_info_query (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), service_name, text);
 }
 
 static void
+on_start_conversation (GdmGreeterLoginWindow *login_window,
+                       const char            *service_name,
+                       GdmGreeterSession     *session)
+{
+        gdm_greeter_client_call_start_conversation (session->priv->client,
+                                                    service_name);
+}
+static void
 on_begin_auto_login (GdmGreeterLoginWindow *login_window,
                      const char            *username,
                      GdmGreeterSession     *session)
@@ -188,26 +218,32 @@ on_begin_auto_login (GdmGreeterLoginWind
 
 static void
 on_begin_verification (GdmGreeterLoginWindow *login_window,
+                       const char            *service_name,
                        GdmGreeterSession     *session)
 {
-        gdm_greeter_client_call_begin_verification (session->priv->client);
+        gdm_greeter_client_call_begin_verification (session->priv->client,
+                                                    service_name);
 }
 
 static void
 on_begin_verification_for_user (GdmGreeterLoginWindow *login_window,
+                                const char            *service_name,
                                 const char            *username,
                                 GdmGreeterSession     *session)
 {
         gdm_greeter_client_call_begin_verification_for_user (session->priv->client,
+                                                             service_name,
                                                              username);
 }
 
 static void
 on_query_answer (GdmGreeterLoginWindow *login_window,
+                 const char            *service_name,
                  const char            *text,
                  GdmGreeterSession     *session)
 {
         gdm_greeter_client_call_answer_query (session->priv->client,
+                                              service_name,
                                               text);
 }
 
@@ -262,9 +298,10 @@ on_disconnected (GdmGreeterLoginWindow *
 
 static void
 on_start_session (GdmGreeterLoginWindow *login_window,
+                  const char            *service_name,
                   GdmGreeterSession     *session)
 {
-        gdm_greeter_client_call_start_session_when_ready (session->priv->client, TRUE);
+        gdm_greeter_client_call_start_session_when_ready (session->priv->client, service_name, TRUE);
 }
 
 static int
@@ -321,7 +358,10 @@ toggle_login_window (GdmGreeterSession *
                 is_local = gdm_greeter_client_get_display_is_local (session->priv->client);
                 g_debug ("GdmGreeterSession: Starting a login window local:%d", is_local);
                 session->priv->login_window = gdm_greeter_login_window_new (is_local);
-
+                g_signal_connect (session->priv->login_window,
+                                  "start-conversation",
+                                  G_CALLBACK (on_start_conversation),
+                                  session);
                 g_signal_connect (session->priv->login_window,
                                   "begin-auto-login",
                                   G_CALLBACK (on_begin_auto_login),
@@ -486,6 +526,64 @@ gdm_greeter_session_event_handler (GdkEv
 }
 
 static void
+on_plugins_loaded (GdmGreeterSession *session)
+{
+        g_debug ("GdmGreeterSession: done loading plugins");
+}
+
+static void
+on_plugin_removed (GdmGreeterSession *session,
+                   GdmGreeterPlugin  *plugin)
+{
+        GdmGreeterExtension *extension;
+
+        extension = gdm_greeter_plugin_get_extension (plugin);
+
+        if (GDM_IS_GREETER_LOGIN_WINDOW_EXTENSION (extension)) {
+                gdm_greeter_login_window_remove_extension (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), extension);
+        }
+        g_object_unref (extension);
+}
+
+static void
+on_plugin_added (GdmGreeterSession *session,
+                 GdmGreeterPlugin  *plugin)
+{
+        GdmGreeterExtension *extension;
+
+        extension = gdm_greeter_plugin_get_extension (plugin);
+
+        if (GDM_IS_GREETER_LOGIN_WINDOW_EXTENSION (extension)) {
+                gdm_greeter_login_window_add_extension (GDM_GREETER_LOGIN_WINDOW (session->priv->login_window), extension);
+        }
+        g_object_unref (extension);
+}
+
+static void
+load_plugins (GdmGreeterSession *session)
+{
+        g_debug ("GdmGreeterSession: loading plugins");
+
+        session->priv->plugin_manager = gdm_plugin_manager_ref_default ();
+
+        g_signal_connect_swapped (session->priv->plugin_manager,
+                                  "plugins-loaded",
+                                  G_CALLBACK (on_plugins_loaded),
+                                  session);
+
+        g_signal_connect_swapped (session->priv->plugin_manager,
+                                  "plugin-added",
+                                  G_CALLBACK (on_plugin_added),
+                                  session);
+
+        g_signal_connect_swapped (session->priv->plugin_manager,
+                                  "plugin-removed",
+                                  G_CALLBACK (on_plugin_removed),
+                                  session);
+
+}
+
+static void
 gdm_greeter_session_init (GdmGreeterSession *session)
 {
         gdm_profile_start (NULL);
@@ -514,6 +612,10 @@ gdm_greeter_session_init (GdmGreeterSess
                           G_CALLBACK (on_ready),
                           session);
         g_signal_connect (session->priv->client,
+                          "conversation-stopped",
+                          G_CALLBACK (on_conversation_stopped),
+                          session);
+        g_signal_connect (session->priv->client,
                           "reset",
                           G_CALLBACK (on_reset),
                           session);
@@ -548,6 +650,8 @@ gdm_greeter_session_init (GdmGreeterSess
         gdk_event_handler_set ((GdkEventFunc) gdm_greeter_session_event_handler,
                                session, NULL);
 
+
+        load_plugins (session);
         gdm_profile_end (NULL);
 }
 
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/gdm-plugin-manager.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-plugin-manager.c	2009-03-04 21:03:53.151447317 -0500
@@ -0,0 +1,478 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#include "config.h"
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include "gdm-plugin-manager.h"
+#include "gdm-greeter-extension.h"
+
+#define GDM_PLUGIN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerPrivate))
+
+typedef struct
+{
+        GModule             *module;
+        char                *filename;
+        GdmGreeterExtension *extension;
+} GdmPluginManagerPlugin;
+
+typedef struct
+{
+        GdmPluginManager *manager;
+        GCancellable     *cancellable;
+} GdmPluginManagerOperation;
+
+struct GdmPluginManagerPrivate
+{
+        GHashTable      *plugins;
+
+        GFileMonitor    *plugin_dir_monitor;
+        GList           *pending_operations;
+};
+
+enum {
+        PLUGINS_LOADED,
+        PLUGIN_ADDED,
+        PLUGIN_REMOVED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void     gdm_plugin_manager_class_init (GdmPluginManagerClass *klass);
+static void     gdm_plugin_manager_init       (GdmPluginManager      *plugin_manager);
+static void     gdm_plugin_manager_finalize   (GObject             *object);
+
+static GObject *plugin_manager_object = NULL;
+
+G_DEFINE_TYPE (GdmPluginManager, gdm_plugin_manager, G_TYPE_OBJECT)
+
+static GdmPluginManagerOperation *
+start_operation (GdmPluginManager *manager)
+{
+        GdmPluginManagerOperation *operation;
+
+        operation = g_new0 (GdmPluginManagerOperation, 1);
+        operation->cancellable = g_cancellable_new ();
+        operation->manager = manager;
+
+        return operation;
+}
+
+static void
+free_operation (GdmPluginManagerOperation *operation)
+{
+        if (operation->cancellable != NULL) {
+                g_object_unref (operation->cancellable);
+                operation->cancellable = NULL;
+        }
+        g_free (operation);
+}
+
+static void
+cancel_operation (GdmPluginManagerOperation *operation)
+{
+        if (operation->cancellable != NULL &&
+            !g_cancellable_is_cancelled (operation->cancellable)) {
+                g_cancellable_cancel (operation->cancellable);
+        }
+
+        free_operation (operation);
+}
+
+static void
+gdm_plugin_manager_track_operation (GdmPluginManager          *manager,
+                                    GdmPluginManagerOperation *operation)
+{
+        manager->priv->pending_operations =
+            g_list_prepend (manager->priv->pending_operations, operation);
+}
+
+static void
+gdm_plugin_manager_untrack_operation (GdmPluginManager          *manager,
+                                     GdmPluginManagerOperation *operation)
+{
+        manager->priv->pending_operations =
+            g_list_remove (manager->priv->pending_operations, operation);
+}
+
+static void
+gdm_plugin_manager_cancel_pending_operations (GdmPluginManager *manager)
+{
+        GList *node;
+
+        node = manager->priv->pending_operations;
+        while (node != NULL) {
+                GList *next_node;
+                GdmPluginManagerOperation *operation;
+
+                operation = node->data;
+                next_node = node->next;
+
+                cancel_operation (operation);
+                manager->priv->pending_operations =
+                    g_list_delete_link (manager->priv->pending_operations,
+                                        node);
+
+                node = next_node;
+        }
+}
+
+static void
+gdm_plugin_manager_class_init (GdmPluginManagerClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gdm_plugin_manager_finalize;
+
+        signals [PLUGINS_LOADED] =
+                g_signal_new ("plugins-loaded",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmPluginManagerClass, plugins_loaded),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+        signals [PLUGIN_ADDED] =
+                g_signal_new ("plugin-added",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmPluginManagerClass, plugin_added),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_GREETER_PLUGIN);
+        signals [PLUGIN_REMOVED] =
+                g_signal_new ("plugin-removed",
+                              G_TYPE_FROM_CLASS (klass),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmPluginManagerClass, plugin_removed),
+                              NULL, NULL,
+                              g_cclosure_marshal_VOID__OBJECT,
+                              G_TYPE_NONE, 1, GDM_TYPE_GREETER_PLUGIN);
+
+        g_type_class_add_private (klass, sizeof (GdmPluginManagerPrivate));
+}
+
+static void
+on_plugin_loaded (GdmPluginManager *manager,
+                  GdmGreeterPlugin *plugin)
+{
+        g_debug ("GdmPluginManager: plugin '%s' loaded.",
+                 gdm_greeter_plugin_get_filename (plugin));
+        g_signal_emit (manager, signals [PLUGIN_ADDED], 0, plugin);
+}
+
+static void
+on_plugin_load_failed (GdmPluginManager *manager,
+                       GdmGreeterPlugin *plugin)
+{
+        const char *filename;
+
+        g_debug ("GdmPluginManager: plugin '%s' could not be loaded.",
+                 gdm_greeter_plugin_get_filename (plugin));
+        filename = gdm_greeter_plugin_get_filename (plugin);
+        g_hash_table_remove (manager->priv->plugins, filename);
+}
+
+static void
+on_plugin_unloaded (GdmPluginManager       *manager,
+                    GdmGreeterPlugin *plugin)
+{
+        const char *filename;
+
+        filename = gdm_greeter_plugin_get_filename (plugin);
+        g_hash_table_remove (manager->priv->plugins, filename);
+}
+
+static void
+load_plugin (GdmPluginManager *manager,
+             const char       *filename)
+{
+        GdmGreeterPlugin *plugin;
+
+        g_debug ("GdmPluginManager: loading plugin '%s'", filename);
+
+        plugin = gdm_greeter_plugin_new (filename);
+
+        g_signal_connect_swapped (plugin, "loaded",
+                                  G_CALLBACK (on_plugin_loaded),
+                                  manager);
+        g_signal_connect_swapped (plugin, "load-failed",
+                                  G_CALLBACK (on_plugin_load_failed),
+                                  manager);
+        g_signal_connect_swapped (plugin, "unloaded",
+                                  G_CALLBACK (on_plugin_unloaded),
+                                  manager);
+        g_hash_table_insert (manager->priv->plugins,
+                             g_strdup (filename), plugin);
+
+        gdm_greeter_plugin_load (plugin);
+}
+
+static void
+on_plugin_info_read (GFileEnumerator           *enumerator,
+                     GAsyncResult              *result,
+                     GdmPluginManagerOperation *operation)
+{
+        GdmPluginManager *manager;
+        GFile *plugin_dir_file;
+        GList *file_list, *node;
+        GError *error;
+
+        manager = operation->manager;
+        error = NULL;
+        file_list = g_file_enumerator_next_files_finish (enumerator,
+                                                         result, &error);
+        plugin_dir_file = g_file_enumerator_get_container (enumerator);
+        if (error != NULL) {
+                char  *plugin_dir;
+
+                plugin_dir = g_file_get_parse_name (plugin_dir_file);
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_debug ("GdmPluginManager: Cancelled reading plugin directory %s",
+                                 plugin_dir);
+                } else {
+                        g_warning ("GdmPluginManager: Unable to read plugin directory %s: %s",
+                                   plugin_dir, error->message);
+                }
+                g_free (plugin_dir);
+                g_error_free (error);
+                g_object_unref (plugin_dir_file);
+                gdm_plugin_manager_untrack_operation (manager, operation);
+                return;
+        }
+
+#ifndef PLUGIN_ORDERING_FIGURED_OUT
+        node = file_list;
+        while (node != NULL) {
+                GFileInfo *info;
+                GFile *file;
+                char *path;
+                GList *next_node;
+
+                next_node = node->next;
+
+                info = (GFileInfo *) node->data;
+
+                file = g_file_get_child (plugin_dir_file,
+                                         g_file_info_get_name (info));
+                path = g_file_get_path (file);
+
+                if (g_str_has_suffix (path, "password.so")) {
+                        file_list = g_list_delete_link (file_list, node);
+                        file_list = g_list_prepend (file_list, info);
+                        next_node = NULL;
+                }
+                g_free (path);
+                g_object_unref (file);
+
+                node = next_node;
+        }
+#endif
+
+        node = file_list;
+        while (node != NULL) {
+                GFileInfo *info;
+                GFile *file;
+                char *path;
+
+                info = (GFileInfo *) node->data;
+
+                file = g_file_get_child (plugin_dir_file,
+                                         g_file_info_get_name (info));
+                path = g_file_get_path (file);
+
+                if (g_str_has_suffix (path, G_MODULE_SUFFIX)) {
+                        load_plugin (manager, path);
+                }
+                g_free (path);
+                g_object_unref (file);
+
+                node = node->next;
+        }
+        g_object_unref (plugin_dir_file);
+
+        gdm_plugin_manager_untrack_operation (manager, operation);
+        g_signal_emit (manager, signals [PLUGINS_LOADED], 0);
+
+        g_list_free (file_list);
+}
+
+static void
+on_plugin_dir_opened (GFile                     *plugin_dir_file,
+                      GAsyncResult              *result,
+                      GdmPluginManagerOperation *open_operation)
+{
+        GdmPluginManager *manager;
+        GFileEnumerator *enumerator;
+        GError *error;
+        GdmPluginManagerOperation *operation;
+
+        manager = open_operation->manager;
+        gdm_plugin_manager_untrack_operation (manager, open_operation);
+
+        error = NULL;
+        enumerator = g_file_enumerate_children_finish (plugin_dir_file,
+                                                       result, &error);
+
+        if (enumerator == NULL) {
+                char *plugin_dir;
+
+                plugin_dir = g_file_get_parse_name (plugin_dir_file);
+
+                if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) {
+                        g_debug ("GdmPluginManager: Cancelled opening plugin directory %s",
+                                 plugin_dir);
+                } else {
+                        g_warning ("GdmPluginManager: Unable to open plugin directory %s: %s",
+                                   plugin_dir, error->message);
+                }
+                g_free (plugin_dir);
+                g_error_free (error);
+                return;
+        }
+
+        operation = start_operation (manager);
+
+        g_file_enumerator_next_files_async (enumerator, G_MAXINT,
+                                            G_PRIORITY_DEFAULT,
+                                            operation->cancellable,
+                                            (GAsyncReadyCallback)
+                                            on_plugin_info_read,
+                                            operation);
+
+        gdm_plugin_manager_track_operation (manager, operation);
+}
+
+static void
+load_plugins_in_dir (GdmPluginManager *manager,
+                     const char       *plugin_dir)
+{
+        GFile *plugin_dir_file;
+        GdmPluginManagerOperation *operation;
+
+        g_debug ("GdmPluginManager: loading plugins in dir '%s'", plugin_dir);
+
+        operation = start_operation (manager);
+        plugin_dir_file = g_file_new_for_path (plugin_dir);
+        g_file_enumerate_children_async (plugin_dir_file, "standard::*",
+                                         G_FILE_QUERY_INFO_NONE,
+                                         G_PRIORITY_DEFAULT,
+                                         operation->cancellable,
+                                         (GAsyncReadyCallback)
+                                         on_plugin_dir_opened,
+                                         operation);
+        g_object_unref (plugin_dir_file);
+        gdm_plugin_manager_track_operation (manager, operation);
+}
+
+static void
+on_plugin_dir_changed (GFileMonitor              *monitor,
+                       GFile                     *file,
+                       GFile                     *other_file,
+                       GFileMonitorEvent          event_type,
+                       GdmPluginManagerOperation *operation)
+{
+}
+
+static void
+watch_plugin_dir (GdmPluginManager *manager,
+                  const char       *plugin_dir)
+{
+
+        GdmPluginManagerOperation *operation;
+        GFile  *file;
+        GError *error;
+
+        operation = start_operation (manager);
+
+        file = g_file_new_for_path (plugin_dir);
+        manager->priv->plugin_dir_monitor = g_file_monitor_directory (file,
+                                                                      G_FILE_MONITOR_NONE,
+                                                                      operation->cancellable,
+                                                                      &error);
+        if (manager->priv->plugin_dir_monitor != NULL) {
+                g_signal_connect (manager->priv->plugin_dir_monitor,
+                                  "changed",
+                                  G_CALLBACK (on_plugin_dir_changed),
+                                  operation);
+                gdm_plugin_manager_track_operation (manager, operation);
+        } else {
+                g_warning ("Unable to monitor %s: %s",
+                           plugin_dir, error->message);
+                g_error_free (error);
+                free_operation (operation);
+        }
+        g_object_unref (file);
+}
+
+static void
+gdm_plugin_manager_init (GdmPluginManager *manager)
+{
+        manager->priv = GDM_PLUGIN_MANAGER_GET_PRIVATE (manager);
+
+        manager->priv->plugins = g_hash_table_new_full (g_str_hash,
+                                                        g_str_equal,
+                                                        g_free,
+                                                        g_object_unref);
+        watch_plugin_dir (manager, GDM_SIMPLE_GREETER_PLUGINS_DIR);
+        load_plugins_in_dir (manager, GDM_SIMPLE_GREETER_PLUGINS_DIR);
+}
+
+static void
+gdm_plugin_manager_finalize (GObject *object)
+{
+        GdmPluginManager *manager;
+
+        manager = GDM_PLUGIN_MANAGER (object);
+
+        g_hash_table_destroy (manager->priv->plugins);
+        g_file_monitor_cancel (manager->priv->plugin_dir_monitor);
+
+        gdm_plugin_manager_cancel_pending_operations (manager);
+
+        G_OBJECT_CLASS (gdm_plugin_manager_parent_class)->finalize (object);
+}
+
+GdmPluginManager *
+gdm_plugin_manager_ref_default (void)
+{
+        if (plugin_manager_object != NULL) {
+                g_object_ref (plugin_manager_object);
+        } else {
+                plugin_manager_object = g_object_new (GDM_TYPE_PLUGIN_MANAGER, NULL);
+                g_object_add_weak_pointer (plugin_manager_object,
+                                           (gpointer *) &plugin_manager_object);
+        }
+
+        return GDM_PLUGIN_MANAGER (plugin_manager_object);
+}
+
+GdmGreeterPlugin *
+gdm_plugin_manager_get_plugin (GdmPluginManager *manager,
+                               const char       *name)
+{
+        return g_hash_table_lookup (manager->priv->plugins, name);
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/gdm-plugin-manager.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-plugin-manager.h	2009-03-04 21:03:53.152449048 -0500
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#ifndef __GDM_PLUGIN_MANAGER_H
+#define __GDM_PLUGIN_MANAGER_H
+
+#include <glib-object.h>
+
+#include "gdm-greeter-plugin.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_PLUGIN_MANAGER         (gdm_plugin_manager_get_type ())
+#define GDM_PLUGIN_MANAGER(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManager))
+#define GDM_PLUGIN_MANAGER_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerClass))
+#define GDM_IS_PLUGIN_MANAGER(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_PLUGIN_MANAGER))
+#define GDM_IS_PLUGIN_MANAGER_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_PLUGIN_MANAGER))
+#define GDM_PLUGIN_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_PLUGIN_MANAGER, GdmPluginManagerClass))
+
+typedef struct GdmPluginManagerPrivate GdmPluginManagerPrivate;
+
+typedef struct
+{
+        GObject                parent;
+        GdmPluginManagerPrivate *priv;
+} GdmPluginManager;
+
+typedef struct
+{
+        GObjectClass   parent_class;
+
+        void          (* plugins_loaded)              (GdmPluginManager *plugin_manager);
+        void          (* plugin_added)                (GdmPluginManager *plugin_manager,
+                                                       GdmGreeterPlugin *plugin);
+        void          (* plugin_removed)              (GdmPluginManager *plugin_manager,
+                                                       GdmGreeterPlugin *plugin);
+} GdmPluginManagerClass;
+
+GType             gdm_plugin_manager_get_type              (void);
+
+GdmPluginManager *gdm_plugin_manager_ref_default           (void);
+
+GdmGreeterPlugin *gdm_plugin_manager_get_plugin            (GdmPluginManager *plugin,
+                                                            const char       *name);
+
+G_END_DECLS
+
+#endif /* __GDM_PLUGIN_MANAGER_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/gdm-task-list.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-task-list.c	2009-03-04 21:03:53.153442188 -0500
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#include "config.h"
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <dirent.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <glib/gstdio.h>
+#include <gtk/gtk.h>
+
+#include "gdm-task-list.h"
+
+#define GDM_TASK_LIST_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), GDM_TYPE_TASK_LIST, GdmTaskListPrivate))
+
+struct GdmTaskListPrivate
+{
+        GtkWidget *box;
+        GList     *tasks;
+};
+
+enum {
+        ACTIVATED = 0,
+        DEACTIVATED,
+        NUMBER_OF_SIGNALS
+};
+
+static guint    signals[NUMBER_OF_SIGNALS];
+
+static void     gdm_task_list_class_init  (GdmTaskListClass *klass);
+static void     gdm_task_list_init        (GdmTaskList      *task_list);
+static void     gdm_task_list_finalize    (GObject          *object);
+
+G_DEFINE_TYPE (GdmTaskList, gdm_task_list, GTK_TYPE_ALIGNMENT);
+
+static void
+on_task_toggled (GdmTaskList    *widget,
+                 GtkRadioButton *button)
+{
+        GdmTask *task;
+
+        task = g_object_get_data (G_OBJECT (button), "gdm-task");
+
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+                g_signal_emit (widget, signals[ACTIVATED], 0, task);
+        } else {
+                g_signal_emit (widget, signals[DEACTIVATED], 0, task);
+        }
+}
+
+GdmTask *
+gdm_task_list_foreach_task (GdmTaskList           *task_list,
+                            GdmTaskListForeachFunc  search_func,
+                            gpointer               data)
+{
+        GList *node;
+
+        for (node = task_list->priv->tasks; node != NULL; node = node->next) {
+                GdmTask *task;
+
+                task = node->data;
+
+                if (search_func (task_list, task, data)) {
+                        return g_object_ref (task);
+                }
+        }
+
+        return NULL;
+}
+
+static void
+on_task_enabled (GdmTaskList *task_list,
+                 GdmTask     *task)
+{
+        GtkWidget *button;
+        GList     *task_node;
+
+        button = g_object_get_data (G_OBJECT (task), "gdm-task-list-button");
+
+        gtk_widget_set_sensitive (button, TRUE);
+
+        /* Sort the list such that the tasks the user clicks last end
+         * up first.  This doesn't change the order in which the tasks
+         * appear in the UI, but will affect which tasks we implicitly
+         * activate if the currently active task gets disabled.
+         */
+        task_node = g_list_find (task_list->priv->tasks, task);
+        if (task_node != NULL) {
+                task_list->priv->tasks = g_list_delete_link (task_list->priv->tasks, task_node);
+                task_list->priv->tasks = g_list_prepend (task_list->priv->tasks,
+                                                         task);
+        }
+}
+
+static void
+activate_first_available_task (GdmTaskList *task_list)
+{
+        GList *node;
+
+        node = task_list->priv->tasks;
+        while (node != NULL) {
+                GdmTask   *task;
+
+                task = GDM_TASK (node->data);
+
+                if (gdm_task_list_set_active_task (task_list, task)) {
+                        break;
+                }
+
+                node = node->next;
+        }
+
+}
+
+static void
+on_task_disabled (GdmTaskList *task_list,
+                  GdmTask     *task)
+{
+        GtkWidget *button;
+        gboolean   was_active;
+
+        button = g_object_get_data (G_OBJECT (task), "gdm-task-list-button");
+        was_active = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+
+        gtk_widget_set_sensitive (button, FALSE);
+
+        if (was_active) {
+                activate_first_available_task (task_list);
+        }
+}
+
+void
+gdm_task_list_add_task (GdmTaskList *task_list,
+                        GdmTask     *task)
+{
+        GtkWidget *image;
+        GtkWidget *button;
+        GIcon     *icon;
+        char      *description;
+
+        if (task_list->priv->tasks == NULL) {
+                button = gtk_radio_button_new (NULL);
+        } else {
+                GdmTask *previous_task;
+                GtkRadioButton *previous_button;
+
+                previous_task = GDM_TASK (task_list->priv->tasks->data);
+                previous_button = GTK_RADIO_BUTTON (g_object_get_data (G_OBJECT (previous_task), "gdm-task-list-button"));
+                button = gtk_radio_button_new_from_widget (previous_button);
+        }
+        g_object_set_data (G_OBJECT (task), "gdm-task-list-button", button);
+
+        g_object_set (G_OBJECT (button), "draw-indicator", FALSE, NULL);
+        g_object_set_data (G_OBJECT (button), "gdm-task", task);
+        g_signal_connect_swapped (button, "toggled",
+                                  G_CALLBACK (on_task_toggled),
+                                  task_list);
+
+        gtk_button_set_focus_on_click (GTK_BUTTON (button), FALSE);
+        gtk_widget_set_sensitive (button, gdm_task_is_enabled (task));
+
+        g_signal_connect_swapped (G_OBJECT (task), "enabled",
+                                  G_CALLBACK (on_task_enabled),
+                                  task_list);
+
+        g_signal_connect_swapped (G_OBJECT (task), "disabled",
+                                  G_CALLBACK (on_task_disabled),
+                                  task_list);
+
+        icon = gdm_task_get_icon (task);
+        image = gtk_image_new_from_gicon (icon, GTK_ICON_SIZE_SMALL_TOOLBAR);
+        g_object_unref (icon);
+
+        gtk_widget_show (image);
+        gtk_container_add (GTK_CONTAINER (button), image);
+        description = gdm_task_get_description (task);
+        gtk_widget_set_tooltip_text (button, description);
+        g_free (description);
+        gtk_widget_show (button);
+
+        gtk_container_add (GTK_CONTAINER (task_list->priv->box), button);
+        task_list->priv->tasks = g_list_append (task_list->priv->tasks, task);
+
+        if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button))) {
+                g_signal_emit (task_list, signals[ACTIVATED], 0, task);
+        }
+}
+
+static void
+gdm_task_list_class_init (GdmTaskListClass *klass)
+{
+        GObjectClass   *object_class = G_OBJECT_CLASS (klass);
+
+        object_class->finalize = gdm_task_list_finalize;
+
+        signals [ACTIVATED] = g_signal_new ("activated",
+                                            G_TYPE_FROM_CLASS (object_class),
+                                            G_SIGNAL_RUN_FIRST,
+                                            G_STRUCT_OFFSET (GdmTaskListClass, activated),
+                                            NULL,
+                                            NULL,
+                                            g_cclosure_marshal_VOID__OBJECT,
+                                            G_TYPE_NONE,
+                                            1, G_TYPE_OBJECT);
+
+        signals [DEACTIVATED] = g_signal_new ("deactivated",
+                                            G_TYPE_FROM_CLASS (object_class),
+                                            G_SIGNAL_RUN_FIRST,
+                                            G_STRUCT_OFFSET (GdmTaskListClass, deactivated),
+                                            NULL,
+                                            NULL,
+                                            g_cclosure_marshal_VOID__OBJECT,
+                                            G_TYPE_NONE,
+                                            1, G_TYPE_OBJECT);
+
+        g_type_class_add_private (klass, sizeof (GdmTaskListPrivate));
+} static void gdm_task_list_init (GdmTaskList *widget)
+{
+        widget->priv = GDM_TASK_LIST_GET_PRIVATE (widget);
+
+        gtk_alignment_set_padding (GTK_ALIGNMENT (widget), 0, 0, 0, 0);
+        gtk_alignment_set (GTK_ALIGNMENT (widget), 0.0, 0.0, 0, 0);
+
+        widget->priv->box = gtk_hbox_new (TRUE, 2);
+        gtk_widget_show (widget->priv->box);
+        gtk_container_add (GTK_CONTAINER (widget),
+                           widget->priv->box);
+}
+
+static void
+gdm_task_list_finalize (GObject *object)
+{
+        GdmTaskList *widget;
+
+        g_return_if_fail (object != NULL);
+        g_return_if_fail (GDM_IS_TASK_LIST (object));
+
+        widget = GDM_TASK_LIST (object);
+
+        g_list_foreach (widget->priv->tasks, (GFunc) g_free, NULL);
+        g_list_free (widget->priv->tasks);
+
+        G_OBJECT_CLASS (gdm_task_list_parent_class)->finalize (object);
+}
+
+GtkWidget *
+gdm_task_list_new (void)
+{
+        GObject *object;
+
+        object = g_object_new (GDM_TYPE_TASK_LIST, NULL);
+
+        return GTK_WIDGET (object);
+}
+
+gboolean
+gdm_task_list_task_is_active (GdmTaskList *task_list,
+                              GdmTask     *task)
+{
+        GtkWidget *button;
+
+        button = g_object_get_data (G_OBJECT (task), "gdm-task-list-button");
+
+        return gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (button));
+}
+
+GdmTask *
+gdm_task_list_get_active_task (GdmTaskList *widget)
+{
+        return gdm_task_list_foreach_task (widget,
+                                        (GdmTaskListForeachFunc)
+                                        gdm_task_list_task_is_active,
+                                        NULL);
+}
+
+gboolean
+gdm_task_list_set_active_task (GdmTaskList *widget,
+                               GdmTask     *task)
+{
+        GtkWidget *button;
+        gboolean   was_sensitive;
+        gboolean   was_activated;
+
+        was_sensitive = GTK_WIDGET_SENSITIVE (widget);
+        gtk_widget_set_sensitive (GTK_WIDGET (widget), TRUE);
+
+        button = GTK_WIDGET (g_object_get_data (G_OBJECT (task),
+                             "gdm-task-list-button"));
+
+        was_activated = FALSE;
+        if (GTK_WIDGET_IS_SENSITIVE (button)) {
+                if (gtk_widget_activate (button)) {
+                        was_activated = TRUE;
+                }
+        }
+
+        gtk_widget_set_sensitive (GTK_WIDGET (widget), was_sensitive);
+        return was_activated;
+}
+
+int
+gdm_task_list_get_number_of_tasks (GdmTaskList *widget)
+{
+        return g_list_length (widget->priv->tasks);
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/gdm-task-list.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/gdm-task-list.h	2009-03-04 21:03:53.154446921 -0500
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ *  Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_TASK_LIST_H
+#define __GDM_TASK_LIST_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <gtk/gtkalignment.h>
+
+#include "gdm-task.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_TASK_LIST         (gdm_task_list_get_type ())
+#define GDM_TASK_LIST(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_TASK_LIST, GdmTaskList))
+#define GDM_TASK_LIST_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_TASK_LIST, GdmTaskListClass))
+#define GDM_IS_TASK_LIST(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_TASK_LIST))
+#define GDM_IS_TASK_LIST_CLASS(k)  (G_TYPE_CHECK_CLASS_TYPE ((k), GDM_TYPE_TASK_LIST))
+#define GDM_TASK_LIST_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDM_TYPE_TASK_LIST, GdmTaskListClass))
+
+typedef struct GdmTaskListPrivate GdmTaskListPrivate;
+typedef struct _GdmTaskList GdmTaskList;
+
+typedef gboolean (* GdmTaskListForeachFunc) (GdmTaskList *task_list,
+                                            GdmTask     *task,
+                                            gpointer     data);
+
+struct _GdmTaskList
+{
+        GtkAlignment             parent;
+        GdmTaskListPrivate *priv;
+};
+
+typedef struct
+{
+        GtkAlignmentClass       parent_class;
+
+        void (* deactivated)      (GdmTaskList *widget,
+                                   GdmTask     *task);
+        void (* activated)      (GdmTaskList *widget,
+                                 GdmTask     *task);
+} GdmTaskListClass;
+
+GType       gdm_task_list_get_type               (void);
+GtkWidget * gdm_task_list_new                    (void);
+
+
+gboolean    gdm_task_list_task_is_active (GdmTaskList *task_list,
+                                          GdmTask     *task);
+GdmTask *   gdm_task_list_get_active_task (GdmTaskList *widget);
+gboolean    gdm_task_list_set_active_task (GdmTaskList *widget,
+                                           GdmTask     *task);
+GdmTask *   gdm_task_list_foreach_task (GdmTaskList           *widget,
+                                     GdmTaskListForeachFunc  foreach_func,
+                                     gpointer               data);
+void        gdm_task_list_add_task        (GdmTaskList *widget,
+                                           GdmTask     *task);
+
+int         gdm_task_list_get_number_of_tasks (GdmTaskList *widget);
+G_END_DECLS
+
+#endif /* __GDM_TASK_LIST_H */
diff -up gdm-2.25.2/gui/simple-greeter/gdm-user-chooser-widget.c.multistack-but-boring gdm-2.25.2/gui/simple-greeter/gdm-user-chooser-widget.c
--- gdm-2.25.2/gui/simple-greeter/gdm-user-chooser-widget.c.multistack-but-boring	2008-09-24 10:05:37.000000000 -0400
+++ gdm-2.25.2/gui/simple-greeter/gdm-user-chooser-widget.c	2009-03-04 21:03:53.156448217 -0500
@@ -233,9 +233,30 @@ gdm_user_chooser_widget_set_show_user_au
 char *
 gdm_user_chooser_widget_get_chosen_user_name (GdmUserChooserWidget *widget)
 {
+        char *active_item_id;
+        gboolean isnt_user;
+
         g_return_val_if_fail (GDM_IS_USER_CHOOSER_WIDGET (widget), NULL);
 
-        return gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget));
+        active_item_id = gdm_chooser_widget_get_active_item (GDM_CHOOSER_WIDGET (widget));
+        if (active_item_id == NULL) {
+                g_debug ("GdmUserChooserWidget: no active item in list");
+                return NULL;
+        }
+
+        gdm_chooser_widget_lookup_item (GDM_CHOOSER_WIDGET (widget), active_item_id,
+                                        NULL, NULL, NULL, NULL, NULL,
+                                        &isnt_user);
+
+        if (isnt_user) {
+                g_debug ("GdmUserChooserWidget: active item '%s' isn't a user", active_item_id);
+                g_free (active_item_id);
+                return NULL;
+        }
+
+        g_debug ("GdmUserChooserWidget: active item '%s' is a user", active_item_id);
+
+        return active_item_id;
 }
 
 void
@@ -340,7 +361,7 @@ add_user (GdmUserChooserWidget *widget,
                 return;
         }
 
-        size = get_icon_height_for_widget (widget);
+        size = get_icon_height_for_widget (GTK_WIDGET (widget));
         pixbuf = gdm_user_render_icon (user, size);
         if (pixbuf == NULL && widget->priv->stock_person_pixbuf != NULL) {
                 pixbuf = g_object_ref (widget->priv->stock_person_pixbuf);
@@ -580,7 +601,7 @@ get_stock_person_pixbuf (GdmUserChooserW
         GdkPixbuf *pixbuf;
         int        size;
 
-        size = get_icon_height_for_widget (widget);
+        size = get_icon_height_for_widget (GTK_WIDGET (widget));
 
         pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
                                            DEFAULT_USER_ICON,
@@ -597,7 +618,7 @@ get_logged_in_pixbuf (GdmUserChooserWidg
         GdkPixbuf *pixbuf;
         int        size;
 
-        size = get_icon_height_for_widget (widget);
+        size = get_icon_height_for_widget (GTK_WIDGET (widget));
 
         pixbuf = gtk_icon_theme_load_icon (widget->priv->icon_theme,
                                            "emblem-default",
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c	2009-03-04 21:03:53.157442475 -0500
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <gtk/gtk.h>
+
+#include "gdm-conversation.h"
+#include "gdm-marshal.h"
+#include "gdm-task.h"
+
+enum {
+        ANSWER,
+        USER_CHOSEN,
+        CANCEL,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_conversation_class_init (gpointer g_iface);
+
+GType
+gdm_conversation_get_type (void)
+{
+        static GType type = 0;
+
+        if (!type) {
+                type = g_type_register_static_simple (G_TYPE_INTERFACE,
+                                                      "GdmConversation",
+                                                      sizeof (GdmConversationIface),
+                                                      (GClassInitFunc) gdm_conversation_class_init,
+                                                      0, NULL, 0);
+
+                g_type_interface_add_prerequisite (type, G_TYPE_OBJECT);
+                g_type_interface_add_prerequisite (type, GDM_TYPE_TASK);
+        }
+
+        return type;
+}
+
+static void
+gdm_conversation_class_init (gpointer g_iface)
+{
+        GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+
+        signals [ANSWER] =
+                g_signal_new ("answer",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmConversationIface, answer),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1, G_TYPE_STRING);
+        signals [USER_CHOSEN] =
+                g_signal_new ("user-chosen",
+                              iface_type,
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmConversationIface, user_chosen),
+                              NULL,
+                              NULL,
+                              gdm_marshal_BOOLEAN__STRING,
+                              G_TYPE_BOOLEAN,
+                              1, G_TYPE_STRING);
+        signals [CANCEL] =
+                g_signal_new ("cancel",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmConversationIface, cancel),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+}
+
+void
+gdm_conversation_set_message  (GdmConversation   *conversation,
+                               const char        *message)
+{
+        GDM_CONVERSATION_GET_IFACE (conversation)->set_message (conversation, message);
+}
+
+void
+gdm_conversation_ask_question (GdmConversation   *conversation,
+                               const char        *message)
+{
+        GDM_CONVERSATION_GET_IFACE (conversation)->ask_question (conversation, message);
+}
+
+void
+gdm_conversation_ask_secret (GdmConversation   *conversation,
+                             const char        *message)
+{
+        GDM_CONVERSATION_GET_IFACE (conversation)->ask_secret (conversation, message);
+}
+
+void
+gdm_conversation_reset (GdmConversation *conversation)
+{
+        return GDM_CONVERSATION_GET_IFACE (conversation)->reset (conversation);
+}
+
+void
+gdm_conversation_set_ready (GdmConversation *conversation)
+{
+        return GDM_CONVERSATION_GET_IFACE (conversation)->set_ready (conversation);
+}
+
+char *
+gdm_conversation_get_service_name (GdmConversation   *conversation)
+{
+        return GDM_CONVERSATION_GET_IFACE (conversation)->get_service_name (conversation);
+}
+
+GtkWidget *
+gdm_conversation_get_page (GdmConversation *conversation)
+{
+        return GDM_CONVERSATION_GET_IFACE (conversation)->get_page (conversation);
+}
+
+GtkActionGroup *
+gdm_conversation_get_actions (GdmConversation *conversation)
+{
+        return GDM_CONVERSATION_GET_IFACE (conversation)->get_actions (conversation);
+}
+
+gboolean
+gdm_conversation_focus (GdmConversation *conversation)
+{
+        return GDM_CONVERSATION_GET_IFACE (conversation)->focus (conversation);
+}
+
+void
+gdm_conversation_request_answer (GdmConversation *conversation)
+{
+        return GDM_CONVERSATION_GET_IFACE (conversation)->request_answer (conversation);
+}
+
+/* protected
+ */
+void
+gdm_conversation_answer (GdmConversation   *conversation,
+                         const char        *answer)
+{
+        g_signal_emit (conversation, signals [ANSWER], 0, answer);
+}
+
+void
+gdm_conversation_cancel (GdmConversation   *conversation)
+{
+        g_signal_emit (conversation, signals [CANCEL], 0);
+}
+
+gboolean
+gdm_conversation_choose_user (GdmConversation *conversation,
+                              const char      *username)
+{
+        gboolean was_chosen;
+
+        was_chosen = FALSE;
+
+        g_signal_emit (conversation, signals [USER_CHOSEN], 0, username, &was_chosen);
+
+        return was_chosen;
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h	2009-03-04 21:03:53.158446161 -0500
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+
+#ifndef __GDM_CONVERSATION_H
+#define __GDM_CONVERSATION_H
+
+#include <glib-object.h>
+#include <gtk/gtk.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_CONVERSATION         (gdm_conversation_get_type ())
+#define GDM_CONVERSATION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_CONVERSATION, GdmConversation))
+#define GDM_CONVERSATION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_CONVERSATION, GdmConversationIface))
+#define GDM_IS_CONVERSATION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_CONVERSATION))
+#define GDM_CONVERSATION_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_CONVERSATION, GdmConversationIface))
+
+#define GDM_CONVERSATION_DEFAULT_ACTION "default-action"
+#define GDM_CONVERSATION_OTHER_USER "__other"
+
+typedef struct _GdmConversation      GdmConversation;
+typedef struct _GdmConversationIface GdmConversationIface;
+
+struct _GdmConversationIface
+{
+        GTypeInterface base_iface;
+
+        /* methods */
+        void   (* set_message)  (GdmConversation *conversation,
+                                 const char      *message);
+        void   (* ask_question) (GdmConversation *conversation,
+                                 const char      *message);
+        void   (* ask_secret)   (GdmConversation *conversation,
+                                 const char      *message);
+        void   (* reset)        (GdmConversation *conversation);
+        void   (* set_ready)    (GdmConversation *conversation);
+        char * (* get_service_name)  (GdmConversation *conversation);
+        GtkWidget * (* get_page) (GdmConversation *conversation);
+        GtkActionGroup * (* get_actions) (GdmConversation *conversation);
+        void   (* request_answer)    (GdmConversation *conversation);
+        gboolean   (* focus)    (GdmConversation *conversation);
+
+        /* signals */
+        char * (* answer)       (GdmConversation *conversation);
+        void   (* cancel)       (GdmConversation *conversation);
+        gboolean  (* user_chosen)  (GdmConversation *conversation);
+};
+
+GType  gdm_conversation_get_type     (void) G_GNUC_CONST;
+
+void   gdm_conversation_set_message  (GdmConversation   *conversation,
+                                      const char        *message);
+void   gdm_conversation_ask_question (GdmConversation   *conversation,
+                                      const char        *message);
+void   gdm_conversation_ask_secret   (GdmConversation   *conversation,
+                                      const char        *message);
+void   gdm_conversation_reset        (GdmConversation   *converastion);
+void   gdm_conversation_set_ready    (GdmConversation   *converastion);
+char  *gdm_conversation_get_service_name   (GdmConversation   *conversation);
+GtkWidget *gdm_conversation_get_page       (GdmConversation   *conversation);
+GtkActionGroup *gdm_conversation_get_actions (GdmConversation   *conversation);
+void   gdm_conversation_request_answer       (GdmConversation   *conversation);
+gboolean   gdm_conversation_focus    (GdmConversation *conversation);
+
+/* protected
+ */
+void   gdm_conversation_answer (GdmConversation   *conversation,
+                                const char        *answer);
+void   gdm_conversation_cancel (GdmConversation   *conversation);
+gboolean  gdm_conversation_choose_user (GdmConversation   *conversation,
+                                        const char        *username);
+
+G_END_DECLS
+
+#endif /* __GDM_CONVERSATION_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c	2009-03-04 21:03:53.159446844 -0500
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-greeter-extension.h"
+
+enum {
+        LOADED,
+        LOAD_FAILED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+
+static void gdm_greeter_extension_class_init (gpointer g_iface);
+
+GType
+gdm_greeter_extension_get_type (void)
+{
+        static GType greeter_extension_type = 0;
+
+        if (!greeter_extension_type) {
+                greeter_extension_type = g_type_register_static_simple (G_TYPE_INTERFACE,
+                                                           "GdmGreeterExtension",
+                                                           sizeof (GdmGreeterExtensionIface),
+                                                           (GClassInitFunc) gdm_greeter_extension_class_init,
+                                                           0, NULL, 0);
+
+                g_type_interface_add_prerequisite (greeter_extension_type, G_TYPE_OBJECT);
+        }
+
+        return greeter_extension_type;
+}
+
+static void
+gdm_greeter_extension_class_init (gpointer g_iface)
+{
+        GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+
+        signals [LOADED] =
+                g_signal_new ("loaded",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterExtensionIface, loaded),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE, 0);
+
+        signals [LOADED] =
+                g_signal_new ("load_failed",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmGreeterExtensionIface, load_failed),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE,
+                              1, G_TYPE_POINTER);
+}
+
+void
+gdm_greeter_extension_loaded (GdmGreeterExtension *extension)
+{
+        g_signal_emit (extension, signals [LOADED], 0);
+}
+
+void
+gdm_greeter_extension_load_failed (GdmGreeterExtension *extension,
+                                   GError              *error)
+{
+        g_signal_emit (extension, signals [LOAD_FAILED], 0, error);
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h	2009-03-04 21:03:53.160442079 -0500
@@ -0,0 +1,55 @@
+/*
+ * Copyright 2009 Red Hat, Inc.  *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode
+ */
+
+#ifndef __GDM_GREETER_EXTENSION_H
+#define __GDM_GREETER_EXTENSION_H
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_GREETER_EXTENSION         (gdm_greeter_extension_get_type ())
+#define GDM_GREETER_EXTENSION(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_GREETER_EXTENSION, GdmGreeterExtension))
+#define GDM_GREETER_EXTENSION_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_GREETER_EXTENSION, GdmGreeterExtensionClass))
+#define GDM_IS_GREETER_EXTENSION(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_GREETER_EXTENSION))
+#define GDM_GREETER_EXTENSION_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_GREETER_EXTENSION, GdmGreeterExtensionIface))
+
+typedef struct _GdmGreeterExtension      GdmGreeterExtension;
+typedef struct _GdmGreeterExtensionIface GdmGreeterExtensionIface;
+
+struct _GdmGreeterExtensionIface
+{
+        GTypeInterface base_iface;
+
+        void (* loaded) (GdmGreeterExtension *extension);
+        void (* load_failed) (GdmGreeterExtension *extension,
+                              GError              *error);
+};
+
+GType gdm_greeter_extension_get_type (void) G_GNUC_CONST;
+
+void gdm_greeter_extension_loaded      (GdmGreeterExtension *extension);
+void gdm_greeter_extension_load_failed (GdmGreeterExtension *extension,
+                                        GError              *error);
+
+typedef GdmGreeterExtension * (* GdmGreeterPluginGetExtensionFunc) (void);
+
+G_END_DECLS
+#endif /* __GDM_GREETER_EXTENSION_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in	2009-03-04 21:03:53.161442133 -0500
@@ -0,0 +1,11 @@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+libdir=@libdir@
+includedir=@includedir@
+pluginsdir=@GDM_SIMPLE_GREETER_PLUGINS_DIR@
+
+Name: GDM Simple Greeter
+Description: Library for GDM Simple Greeter Plugins
+Version: @VERSION@
+Libs: -L${libdir} -lgdmsimplegreeter
+Cflags: -I${includedir}/gdm/simple-greeter
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c	2009-03-04 21:03:53.162442257 -0500
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "gdm-task.h"
+
+enum {
+        ENABLED,
+        DISABLED,
+        LAST_SIGNAL
+};
+
+static guint signals [LAST_SIGNAL] = { 0, };
+static void gdm_task_class_init (gpointer g_iface);
+
+GType
+gdm_task_get_type (void)
+{
+        static GType task_type = 0;
+
+        if (!task_type) {
+                task_type = g_type_register_static_simple (G_TYPE_INTERFACE,
+                                                           "GdmTask",
+                                                           sizeof (GdmTaskIface),
+                                                           (GClassInitFunc) gdm_task_class_init,
+                                                           0, NULL, 0);
+
+                g_type_interface_add_prerequisite (task_type, G_TYPE_OBJECT);
+        }
+
+        return task_type;
+}
+
+GIcon *
+gdm_task_get_icon (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->get_icon (task);
+}
+
+char *
+gdm_task_get_description (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->get_description (task);
+}
+
+char *
+gdm_task_get_name (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->get_name (task);
+}
+
+void
+gdm_task_set_enabled (GdmTask   *task,
+                      gboolean   should_enable)
+{
+        g_object_set_data (G_OBJECT (task), "gdm-task-is-disabled", GINT_TO_POINTER (!should_enable));
+
+        if (should_enable) {
+                g_signal_emit (G_OBJECT (task), signals [ENABLED], 0);
+        } else {
+                g_signal_emit (G_OBJECT (task), signals [DISABLED], 0);
+        }
+}
+
+gboolean
+gdm_task_is_enabled (GdmTask   *task)
+{
+        return !g_object_get_data (G_OBJECT (task), "gdm-task-is-disabled");
+}
+
+gboolean
+gdm_task_is_choosable (GdmTask *task)
+{
+        return GDM_TASK_GET_IFACE (task)->is_choosable (task);
+}
+
+static void
+gdm_task_class_init (gpointer g_iface)
+{
+        GType iface_type = G_TYPE_FROM_INTERFACE (g_iface);
+
+        signals [ENABLED] =
+                g_signal_new ("enabled",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmTaskIface, enabled),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
+
+        signals [DISABLED] =
+                g_signal_new ("disabled",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmTaskIface, disabled),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__VOID,
+                              G_TYPE_NONE,
+                              0);
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h	2009-03-04 21:03:53.163442172 -0500
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written by: Ray Strode <rstrode@redhat.com>
+ */
+
+
+#ifndef __GDM_TASK_H
+#define __GDM_TASK_H
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_TASK         (gdm_task_get_type ())
+#define GDM_TASK(o)           (G_TYPE_CHECK_INSTANCE_CAST ((o), GDM_TYPE_TASK, GdmTask))
+#define GDM_TASK_CLASS(k)     (G_TYPE_CHECK_CLASS_CAST((k), GDM_TYPE_TASK, GdmTaskIface))
+#define GDM_IS_TASK(o)        (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDM_TYPE_TASK))
+#define GDM_TASK_GET_IFACE(o) (G_TYPE_INSTANCE_GET_INTERFACE ((o), GDM_TYPE_TASK, GdmTaskIface))
+
+typedef struct _GdmTask      GdmTask;
+typedef struct _GdmTaskIface GdmTaskIface;
+
+struct _GdmTaskIface
+{
+        GTypeInterface base_iface;
+
+        /* methods */
+        GIcon * (* get_icon)        (GdmTask   *task);
+        char *  (* get_description) (GdmTask   *task);
+        char *  (* get_name)        (GdmTask   *task);
+        gboolean  (* is_choosable)    (GdmTask   *task);
+        /* signals */
+        void (* enabled) (GdmTask *task);
+        void (* disabled) (GdmTask *task);
+};
+
+GType  gdm_task_get_type        (void) G_GNUC_CONST;
+
+GIcon *gdm_task_get_icon        (GdmTask   *task);
+char  *gdm_task_get_description (GdmTask   *task);
+char  *gdm_task_get_name        (GdmTask   *task);
+void   gdm_task_set_enabled     (GdmTask   *task,
+                                 gboolean   should_enable);
+gboolean   gdm_task_is_enabled     (GdmTask   *task);
+gboolean   gdm_task_is_choosable   (GdmTask   *task);
+G_END_DECLS
+
+#endif /* __GDM_TASK_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/libgdmsimplegreeter/Makefile.am	2009-03-04 21:03:53.164444252 -0500
@@ -0,0 +1,48 @@
+NULL =
+
+AM_CPPFLAGS = \
+	-I.					\
+	-I..					\
+	-I$(top_srcdir)/common			\
+	-DBINDIR=\"$(bindir)\"			\
+	-DDATADIR=\"$(datadir)\"		\
+	-DLIBDIR=\"$(libdir)\"			\
+	-DLIBEXECDIR=\"$(libexecdir)\"		\
+	-DLOGDIR=\"$(logdir)\"			\
+	-DPIXMAPDIR=\"$(pixmapdir)\"		\
+	-DSBINDIR=\"$(sbindir)\"		\
+	$(GTK_CFLAGS)				\
+	$(NULL)
+
+lib_LTLIBRARIES = 			\
+	libgdmsimplegreeter.la		\
+	$(NULL)
+
+libgdmsimplegreeter_la_SOURCES =		\
+	gdm-task.h				\
+	gdm-task.c				\
+	gdm-conversation.h			\
+	gdm-conversation.c			\
+	gdm-greeter-extension.h			\
+	gdm-greeter-extension.c			\
+	$(NULL)
+
+libgdmsimplegreeter_la_LIBADD =			\
+	$(GTK_LIBS)				\
+	$(top_builddir)/common/libgdmcommon.la	\
+	$(NULL)
+
+libgdmsimplegreeter_la_LDFLAGS = 		\
+	-export-symbols-regex '^[^_].*'		\
+	-version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \
+	-no-undefined				\
+	$(NULL)
+
+headersdir = $(includedir)/gdm/simple-greeter
+headers_HEADERS = gdm-greeter-extension.h
+
+pkgconfigdir = $(libdir)/pkgconfig
+pkgconfig_DATA = gdmsimplegreeter.pc
+
+EXTRA_DIST = gdmsimplegreeter.pc
+MAINTAINERCLEANFILES = Makefile.in
diff -up gdm-2.25.2/gui/simple-greeter/Makefile.am.multistack-but-boring gdm-2.25.2/gui/simple-greeter/Makefile.am
--- gdm-2.25.2/gui/simple-greeter/Makefile.am.multistack-but-boring	2008-12-03 00:22:20.000000000 -0500
+++ gdm-2.25.2/gui/simple-greeter/Makefile.am	2009-03-04 21:03:53.165451360 -0500
@@ -2,11 +2,14 @@ NULL =
 
 SUBDIRS = 				\
 	libnotificationarea		\
+	libgdmsimplegreeter		\
+	plugins				\
 	$(NULL)
 
 AM_CPPFLAGS = \
 	-I$(top_srcdir)/common				\
 	-I$(top_srcdir)/gui/simple-greeter/libnotificationarea	\
+	-I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter	\
 	-DDMCONFDIR=\""$(dmconfdir)"\"			\
 	-DGDMCONFDIR=\"$(gdmconfdir)\"                  \
 	-DDATADIR=\""$(datadir)"\"		 	\
@@ -17,6 +20,7 @@ AM_CPPFLAGS = \
 	-DLIBEXECDIR=\""$(libexecdir)"\" 		\
 	-DSBINDIR=\""$(sbindir)"\"		 	\
 	-DAT_SPI_REGISTRYD_DIR="\"$(AT_SPI_REGISTRYD_DIR)\""	\
+	-DGDM_SIMPLE_GREETER_PLUGINS_DIR="\"$(GDM_SIMPLE_GREETER_PLUGINS_DIR)\""\
 	$(DISABLE_DEPRECATED_CFLAGS)	\
 	$(GTK_CFLAGS)					\
 	$(SIMPLE_GREETER_CFLAGS)			\
@@ -83,10 +87,17 @@ test_greeter_login_window_SOURCES = 	\
 	gdm-user-chooser-widget.c	\
 	gdm-user-chooser-dialog.h	\
 	gdm-user-chooser-dialog.c	\
+	gdm-task-list.h			\
+	gdm-task-list.c			\
+	gdm-plugin-manager.h		\
+	gdm-plugin-manager.c		\
+	gdm-greeter-plugin.h		\
+	gdm-greeter-plugin.c		\
 	$(NULL)
 
 test_greeter_login_window_LDADD =	\
 	$(top_builddir)/common/libgdmcommon.la	\
+	$(top_builddir)/gui/simple-greeter/libgdmsimplegreeter/libgdmsimplegreeter.la	\
 	libgdmuser.la			\
 	$(COMMON_LIBS)			\
 	$(SIMPLE_GREETER_LIBS)		\
@@ -138,6 +149,7 @@ test_greeter_panel_SOURCES = 	\
 test_greeter_panel_LDADD =	\
 	$(top_builddir)/common/libgdmcommon.la	\
 	$(top_builddir)/gui/simple-greeter/libnotificationarea/libnotificationarea.la	\
+	$(top_builddir)/gui/simple-greeter/libgdmsimplegreeter/libgdmsimplegreeter.la	\
 	$(SIMPLE_GREETER_LIBS)		\
 	$(GTK_LIBS)			\
 	$(GCONF_LIBS)			\
@@ -310,18 +322,25 @@ gdm_simple_greeter_SOURCES =  		\
 	gdm-language-chooser-dialog.c	\
 	gdm-language-option-widget.h	\
 	gdm-language-option-widget.c	\
+	gdm-plugin-manager.h		\
+	gdm-plugin-manager.c		\
 	gdm-sessions.h			\
 	gdm-sessions.c			\
 	gdm-session-option-widget.h	\
 	gdm-session-option-widget.c	\
+	gdm-greeter-plugin.h		\
+	gdm-greeter-plugin.c		\
 	gdm-user-chooser-widget.h	\
 	gdm-user-chooser-widget.c	\
+	gdm-task-list.h			\
+	gdm-task-list.c			\
 	$(NULL)
 
 gdm_simple_greeter_LDADD = 		\
 	$(top_builddir)/common/libgdmcommon.la	\
 	libgdmuser.la			\
 	$(top_builddir)/gui/simple-greeter/libnotificationarea/libnotificationarea.la	\
+	$(top_builddir)/gui/simple-greeter/libgdmsimplegreeter/libgdmsimplegreeter.la	\
 	$(COMMON_LIBS)			\
 	$(EXTRA_GREETER_LIBS)   	\
 	$(SIMPLE_GREETER_LIBS)		\
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c	2009-03-04 21:03:53.167443367 -0500
@@ -0,0 +1,304 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-fingerprint-extension.h"
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+struct _GdmFingerprintExtensionPrivate
+{
+        GIcon     *icon;
+        GtkWidget *page;
+        GtkActionGroup *actions;
+
+        GtkWidget *message_label;
+        GtkWidget *prompt_label;
+        GtkWidget *prompt_entry;
+
+        guint      answer_pending : 1;
+};
+
+static void gdm_fingerprint_extension_finalize (GObject *object);
+
+static void gdm_task_iface_init (GdmTaskIface *iface);
+static void gdm_conversation_iface_init (GdmConversationIface *iface);
+static void gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmFingerprintExtension,
+                         gdm_fingerprint_extension,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_GREETER_EXTENSION,
+                                                gdm_greeter_extension_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_TASK,
+                                                gdm_task_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_CONVERSATION,
+                                                gdm_conversation_iface_init));
+
+static void
+gdm_fingerprint_extension_set_message (GdmConversation *conversation,
+                                       const char *message)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->message_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message);
+}
+
+static void
+gdm_fingerprint_extension_ask_question (GdmConversation *conversation,
+                                        const char      *message)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_fingerprint_extension_ask_secret (GdmConversation *conversation,
+                                      const char      *message)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_fingerprint_extension_reset (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        gtk_widget_hide (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        extension->priv->answer_pending = FALSE;
+
+        gdm_task_set_enabled (GDM_TASK (conversation), FALSE);
+}
+
+static void
+gdm_fingerprint_extension_set_ready (GdmConversation *conversation)
+{
+        gdm_task_set_enabled (GDM_TASK (conversation), TRUE);
+}
+
+char *
+gdm_fingerprint_extension_get_service_name (GdmConversation *conversation)
+{
+        return g_strdup (PAMSERVICENAME);
+}
+
+GtkWidget *
+gdm_fingerprint_extension_get_page (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        return extension->priv->page;
+}
+
+GtkActionGroup *
+gdm_fingerprint_extension_get_actions (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+
+        return g_object_ref (extension->priv->actions);
+}
+
+void
+gdm_fingerprint_extension_request_answer (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+        const char *text;
+
+        if (!extension->priv->answer_pending) {
+                gdm_conversation_answer (conversation, NULL);
+                return;
+        }
+
+        extension->priv->answer_pending = FALSE;
+        text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry));
+        gdm_conversation_answer (conversation, text);
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+}
+
+gboolean
+gdm_fingerprint_extension_focus (GdmConversation *conversation)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (conversation);
+
+        if (!extension->priv->answer_pending) {
+                return FALSE;
+        }
+
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        return TRUE;
+}
+
+GIcon *
+gdm_fingerprint_extension_get_icon (GdmTask *task)
+{
+        GdmFingerprintExtension *extension = GDM_FINGERPRINT_EXTENSION (task);
+        return g_object_ref (extension->priv->icon);
+}
+
+char *
+gdm_fingerprint_extension_get_name (GdmTask *task)
+{
+        return g_strdup (_("Fingerprint Authentication"));
+}
+
+char *
+gdm_fingerprint_extension_get_description (GdmTask *task)
+{
+        return g_strdup (_("Log into session with fingerprint"));
+}
+
+gboolean
+gdm_fingerprint_extension_is_choosable (GdmTask *task)
+{
+        return FALSE;
+}
+
+static void
+gdm_task_iface_init (GdmTaskIface *iface)
+{
+        iface->get_icon = gdm_fingerprint_extension_get_icon;
+        iface->get_description = gdm_fingerprint_extension_get_description;
+        iface->get_name = gdm_fingerprint_extension_get_name;
+        iface->is_choosable = gdm_fingerprint_extension_is_choosable;
+}
+
+static void
+gdm_conversation_iface_init (GdmConversationIface *iface)
+{
+        iface->set_message = gdm_fingerprint_extension_set_message;
+        iface->ask_question = gdm_fingerprint_extension_ask_question;
+        iface->ask_secret = gdm_fingerprint_extension_ask_secret;
+        iface->reset = gdm_fingerprint_extension_reset;
+        iface->set_ready = gdm_fingerprint_extension_set_ready;
+        iface->get_service_name = gdm_fingerprint_extension_get_service_name;
+        iface->get_page = gdm_fingerprint_extension_get_page;
+        iface->get_actions = gdm_fingerprint_extension_get_actions;
+        iface->request_answer = gdm_fingerprint_extension_request_answer;
+        iface->focus = gdm_fingerprint_extension_focus;
+}
+
+static void
+gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface)
+{
+}
+
+static void
+gdm_fingerprint_extension_class_init (GdmFingerprintExtensionClass *extension_class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (extension_class);
+
+        object_class->finalize = gdm_fingerprint_extension_finalize;
+
+        g_type_class_add_private (extension_class,
+                                  sizeof (GdmFingerprintExtensionPrivate));
+}
+
+static void
+gdm_fingerprint_extension_finalize (GObject *object)
+{
+}
+
+static void
+create_page (GdmFingerprintExtension *extension)
+{
+        GtkBuilder *builder;
+        GObject *object;
+        GError *error;
+
+        builder = gtk_builder_new ();
+
+        error = NULL;
+        gtk_builder_add_from_file (builder,
+                                   PLUGINDATADIR "/page.ui",
+                                   &error);
+
+        if (error != NULL) {
+                g_warning ("Could not load UI file: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        object = gtk_builder_get_object (builder, "page");
+        g_object_ref (object);
+
+        extension->priv->page = GTK_WIDGET (object);
+
+        object = gtk_builder_get_object (builder, "auth-prompt-label");
+        g_object_ref (object);
+        extension->priv->prompt_label = GTK_WIDGET (object);
+        gtk_widget_hide (extension->priv->prompt_label);
+
+        object = gtk_builder_get_object (builder, "auth-prompt-entry");
+        g_object_ref (object);
+        extension->priv->prompt_entry = GTK_WIDGET (object);
+        gtk_widget_hide (extension->priv->prompt_entry);
+
+        object = gtk_builder_get_object (builder, "auth-message-label");
+        g_object_ref (object);
+        extension->priv->message_label = GTK_WIDGET (object);
+        gtk_widget_show (extension->priv->message_label);
+
+        g_object_unref (builder);
+}
+
+static void
+create_actions (GdmFingerprintExtension *extension)
+{
+        extension->priv->actions = gtk_action_group_new ("gdm-fingerprint-extension");
+}
+
+static void
+gdm_fingerprint_extension_init (GdmFingerprintExtension *extension)
+{
+        extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension,
+                                                       GDM_TYPE_FINGERPRINT_EXTENSION,
+                                                       GdmFingerprintExtensionPrivate);
+
+        extension->priv->icon = g_themed_icon_new ("stock_allow-effects");
+        create_page (extension);
+        create_actions (extension);
+        gdm_fingerprint_extension_reset (GDM_CONVERSATION (extension));
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h	2009-03-04 21:03:53.168442094 -0500
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_FINGERPRINT_EXTENSION_H
+#define __GDM_FINGERPRINT_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_FINGERPRINT_EXTENSION (gdm_fingerprint_extension_get_type ())
+#define GDM_FINGERPRINT_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtension))
+#define GDM_FINGERPRINT_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtensionClass))
+#define GDM_IS_FINGERPRINT_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_FINGERPRINT_EXTENSION))
+#define GDM_IS_FINGERPRINT_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_FINGERPRINT_EXTENSION))
+#define GDM_FINGERPRINT_EXTENSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_FINGERPRINT_EXTENSION, GdmFingerprintExtensionClass))
+
+typedef struct _GdmFingerprintExtensionPrivate GdmFingerprintExtensionPrivate;
+
+typedef struct
+{
+        GObject                  parent;
+        GdmFingerprintExtensionPrivate *priv;
+} GdmFingerprintExtension;
+
+typedef struct
+{
+        GObjectClass parent_class;
+} GdmFingerprintExtensionClass;
+
+GType                 gdm_fingerprint_extension_get_type      (void);
+
+GdmFingerprintExtension *gdm_fingerprint_extension_new       (void);
+
+G_END_DECLS
+
+#endif /* GDM_FINGERPRINT_EXTENSION_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint	2009-03-04 21:03:53.169450949 -0500
@@ -0,0 +1,10 @@
+#%PAM-1.0
+auth       required    pam_fprintd.so
+account    required    pam_nologin.so
+account    include     system-auth
+password   include     system-auth
+session    required    pam_loginuid.so
+session    optional    pam_console.so
+session    optional    pam_keyinit.so force revoke
+session    required    pam_namespace.so
+session    include     system-auth
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/Makefile.am
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/Makefile.am	2009-03-04 21:03:53.170444507 -0500
@@ -0,0 +1,50 @@
+NULL =
+
+extensiondir = $(extensionsdatadir)/fingerprint
+extension_DATA = page.ui
+
+pamservicename = gdm-fingerprint
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/common				\
+	-I$(top_srcdir)/gui/simple-greeter/libnotificationarea	\
+	-I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter	\
+	-DDMCONFDIR=\""$(dmconfdir)"\"			\
+	-DGDMCONFDIR=\"$(gdmconfdir)\"                  \
+	-DPLUGINDATADIR=\""$(extensiondir)"\"		\
+	-DPAMSERVICENAME=\""$(pamservicename)"\"	\
+	-DSYSCONFDIR=\""$(sysconfdir)"\"		\
+	-DLIBLOCALEDIR=\""$(prefix)/lib/locale"\"	\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\" 	\
+	-DLIBEXECDIR=\""$(libexecdir)"\" 		\
+	-DSBINDIR=\""$(sbindir)"\"		 	\
+	$(DISABLE_DEPRECATED_CFLAGS)	\
+	$(GTK_CFLAGS)					\
+	$(SIMPLE_GREETER_CFLAGS)			\
+	$(POLKIT_GNOME_CFLAGS)				\
+	$(NULL)
+
+
+plugindir = $(GDM_SIMPLE_GREETER_PLUGINS_DIR)
+plugin_LTLIBRARIES = fingerprint.la
+
+fingerprint_la_CFLAGS =			\
+	$(SIMPLE_GREETER_CFLAGS)	\
+	$(NULL)
+
+fingerprint_la_LDFLAGS = -module -avoid-version -export-dynamic
+fingerprint_la_LIBADD = ../../../../common/libgdmcommon.la \
+			../../libgdmsimplegreeter/libgdmsimplegreeter.la
+fingerprint_la_SOURCES =				\
+			gdm-fingerprint-extension.h	\
+			gdm-fingerprint-extension.c	\
+			plugin.c
+
+pamdir = $(PAM_PREFIX)/pam.d
+pam_DATA = $(pamservicename)
+
+EXTRA_DIST = $(extension_DATA) $(pam_DATA)
+
+MAINTAINERCLEANFILES =                  \
+        *~                              \
+        Makefile.in
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/page.ui
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/page.ui	2009-03-04 21:03:53.171445819 -0500
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.14"/>
+    <object class="GtkVBox" id="page">
+      <property name="visible">True</property>
+      <property name="orientation">vertical</property>
+      <child>
+        <object class="GtkHBox" id="auth-input-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-prompt-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="expand">False</property>
+              <property name="fill">False</property>
+              <property name="position">0</property>
+            </packing>
+          </child>
+          <child>
+            <object class="GtkEntry" id="auth-prompt-entry">
+              <property name="visible">True</property>
+              <property name="can_focus">True</property>
+              <property name="activates_default">True</property>
+            </object>
+            <packing>
+              <property name="position">1</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">0</property>
+        </packing>
+      </child>
+      <child>
+        <object class="GtkHBox" id="auth-message-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-message-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="position">0</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">1</property>
+        </packing>
+      </child>
+    </object>
+</interface>
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/plugin.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/fingerprint/plugin.c	2009-03-04 21:03:53.172450483 -0500
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include "gdm-fingerprint-extension.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (void)
+{
+        static GObject *extension;
+
+        if (extension != NULL) {
+                g_object_ref (extension);
+        } else {
+                extension = g_object_new (GDM_TYPE_FINGERPRINT_EXTENSION, NULL);
+                g_object_add_weak_pointer (extension, (gpointer *) &extension);
+        }
+
+        return GDM_GREETER_EXTENSION (extension);
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/Makefile.am
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/Makefile.am	2009-03-04 21:03:53.173442226 -0500
@@ -0,0 +1 @@
+SUBDIRS = password
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/password/gdm-password-extension.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/password/gdm-password-extension.c	2009-03-04 21:03:53.174446121 -0500
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-password-extension.h"
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+struct _GdmPasswordExtensionPrivate
+{
+        GIcon     *icon;
+        GtkWidget *page;
+        GtkActionGroup *actions;
+
+        GtkWidget *message_label;
+        GtkWidget *prompt_label;
+        GtkWidget *prompt_entry;
+
+        guint      answer_pending : 1;
+};
+
+static void gdm_password_extension_finalize (GObject *object);
+
+static void gdm_task_iface_init (GdmTaskIface *iface);
+static void gdm_conversation_iface_init (GdmConversationIface *iface);
+static void gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmPasswordExtension,
+                         gdm_password_extension,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_GREETER_EXTENSION,
+                                                gdm_greeter_extension_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_TASK,
+                                                gdm_task_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_CONVERSATION,
+                                                gdm_conversation_iface_init));
+
+static void
+gdm_password_extension_set_message (GdmConversation *conversation,
+                                    const char *message)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->message_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message);
+}
+
+static void
+gdm_password_extension_ask_question (GdmConversation *conversation,
+                                     const char      *message)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_password_extension_ask_secret (GdmConversation *conversation,
+                                   const char      *message)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_password_extension_reset (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        gtk_widget_hide (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        extension->priv->answer_pending = FALSE;
+
+        gdm_task_set_enabled (GDM_TASK (conversation), FALSE);
+}
+
+static void
+gdm_password_extension_set_ready (GdmConversation *conversation)
+{
+        gdm_task_set_enabled (GDM_TASK (conversation), TRUE);
+}
+
+char *
+gdm_password_extension_get_service_name (GdmConversation *conversation)
+{
+        return g_strdup (PAMSERVICENAME);
+}
+
+GtkWidget *
+gdm_password_extension_get_page (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        return extension->priv->page;
+}
+
+GtkActionGroup *
+gdm_password_extension_get_actions (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        return g_object_ref (extension->priv->actions);
+}
+
+void
+gdm_password_extension_request_answer (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        const char *text;
+
+        if (!extension->priv->answer_pending) {
+                gdm_conversation_answer (conversation, NULL);
+                return;
+        }
+
+        extension->priv->answer_pending = FALSE;
+        text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry));
+        gdm_conversation_answer (conversation, text);
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_widget_hide (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+}
+
+gboolean
+gdm_password_extension_focus (GdmConversation *conversation)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (conversation);
+        if (!extension->priv->answer_pending) {
+                gdm_conversation_answer (conversation, NULL);
+                return FALSE;
+        }
+
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        return TRUE;
+}
+
+GIcon *
+gdm_password_extension_get_icon (GdmTask *task)
+{
+        GdmPasswordExtension *extension = GDM_PASSWORD_EXTENSION (task);
+        return g_object_ref (extension->priv->icon);
+}
+
+char *
+gdm_password_extension_get_name (GdmTask *task)
+{
+        return g_strdup (_("Password Authentication"));
+}
+
+char *
+gdm_password_extension_get_description (GdmTask *task)
+{
+        return g_strdup (_("Log into session with username and password"));
+}
+
+gboolean
+gdm_password_extension_is_choosable (GdmTask *task)
+{
+        return FALSE;
+}
+
+static void
+gdm_task_iface_init (GdmTaskIface *iface)
+{
+        iface->get_icon = gdm_password_extension_get_icon;
+        iface->get_description = gdm_password_extension_get_description;
+        iface->get_name = gdm_password_extension_get_name;
+        iface->is_choosable = gdm_password_extension_is_choosable;
+}
+
+static void
+gdm_conversation_iface_init (GdmConversationIface *iface)
+{
+        iface->set_message = gdm_password_extension_set_message;
+        iface->ask_question = gdm_password_extension_ask_question;
+        iface->ask_secret = gdm_password_extension_ask_secret;
+        iface->reset = gdm_password_extension_reset;
+        iface->set_ready = gdm_password_extension_set_ready;
+        iface->get_service_name = gdm_password_extension_get_service_name;
+        iface->get_page = gdm_password_extension_get_page;
+        iface->get_actions = gdm_password_extension_get_actions;
+        iface->request_answer = gdm_password_extension_request_answer;
+        iface->focus = gdm_password_extension_focus;
+}
+
+static void
+gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface)
+{
+}
+
+static void
+gdm_password_extension_class_init (GdmPasswordExtensionClass *extension_class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (extension_class);
+
+        object_class->finalize = gdm_password_extension_finalize;
+
+        g_type_class_add_private (extension_class,
+                                  sizeof (GdmPasswordExtensionPrivate));
+}
+
+static void
+gdm_password_extension_finalize (GObject *object)
+{
+}
+
+static void
+on_activate_log_in (GdmPasswordExtension *extension)
+{
+        gdm_password_extension_request_answer (GDM_CONVERSATION (extension));
+}
+
+static void
+create_page (GdmPasswordExtension *extension)
+{
+        GtkBuilder *builder;
+        GObject *object;
+        GError *error;
+
+        builder = gtk_builder_new ();
+
+        error = NULL;
+        gtk_builder_add_from_file (builder,
+                                   PLUGINDATADIR "/page.ui",
+                                   &error);
+
+        if (error != NULL) {
+                g_warning ("Could not load UI file: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        object = gtk_builder_get_object (builder, "page");
+        g_object_ref (object);
+
+        extension->priv->page = GTK_WIDGET (object);
+
+        object = gtk_builder_get_object (builder, "auth-prompt-label");
+        g_object_ref (object);
+        extension->priv->prompt_label = GTK_WIDGET (object);
+        gtk_widget_hide (extension->priv->prompt_label);
+
+        object = gtk_builder_get_object (builder, "auth-prompt-entry");
+        g_object_ref (object);
+        extension->priv->prompt_entry = GTK_WIDGET (object);
+        gtk_widget_hide (extension->priv->prompt_entry);
+
+        object = gtk_builder_get_object (builder, "auth-message-label");
+        g_object_ref (object);
+        extension->priv->message_label = GTK_WIDGET (object);
+        gtk_widget_show (extension->priv->message_label);
+
+        g_object_unref (builder);
+}
+
+static void
+create_actions (GdmPasswordExtension *extension)
+{
+        GtkAction *action;
+
+        extension->priv->actions = gtk_action_group_new ("gdm-password-extension");
+
+        action = gtk_action_new (GDM_CONVERSATION_DEFAULT_ACTION,
+                                 _("Log In"),
+                                 _("Log into the currently selected sesson"),
+                                 NULL);
+        g_signal_connect_swapped (action, "activate",
+                                  G_CALLBACK (on_activate_log_in), extension);
+        g_object_set (G_OBJECT (action), "icon-name", "go-home", NULL);
+        gtk_action_group_add_action (extension->priv->actions,
+                                     action);
+}
+
+static void
+gdm_password_extension_init (GdmPasswordExtension *extension)
+{
+        extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension,
+                                                       GDM_TYPE_PASSWORD_EXTENSION,
+                                                       GdmPasswordExtensionPrivate);
+
+        extension->priv->icon = g_themed_icon_new ("dialog-password");
+        create_page (extension);
+        create_actions (extension);
+
+        gdm_password_extension_reset (GDM_CONVERSATION (extension));
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/password/gdm-password-extension.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/password/gdm-password-extension.h	2009-03-04 21:03:53.175446036 -0500
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_PASSWORD_EXTENSION_H
+#define __GDM_PASSWORD_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_PASSWORD_EXTENSION (gdm_password_extension_get_type ())
+#define GDM_PASSWORD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtension))
+#define GDM_PASSWORD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtensionClass))
+#define GDM_IS_PASSWORD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_PASSWORD_EXTENSION))
+#define GDM_IS_PASSWORD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_PASSWORD_EXTENSION))
+#define GDM_PASSWORD_EXTENSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_PASSWORD_EXTENSION, GdmPasswordExtensionClass))
+
+typedef struct _GdmPasswordExtensionPrivate GdmPasswordExtensionPrivate;
+
+typedef struct
+{
+        GObject                  parent;
+        GdmPasswordExtensionPrivate *priv;
+} GdmPasswordExtension;
+
+typedef struct
+{
+        GObjectClass parent_class;
+} GdmPasswordExtensionClass;
+
+GType                 gdm_password_extension_get_type      (void);
+
+GdmPasswordExtension *gdm_password_extension_new       (void);
+
+G_END_DECLS
+
+#endif /* GDM_PASSWORD_EXTENSION_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/password/gdm-password
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/password/gdm-password	2009-03-04 21:03:53.176442109 -0500
@@ -0,0 +1,13 @@
+#%PAM-1.0
+auth       required    pam_env.so
+auth       required    pam_unix.so
+auth       optional    pam_gnome_keyring.so
+account    required    pam_nologin.so
+account    include     system-auth
+password   include     system-auth
+session    required    pam_loginuid.so
+session    optional    pam_console.so
+session    optional    pam_keyinit.so force revoke
+session    required    pam_namespace.so
+session    optional    pam_gnome_keyring.so auto_start
+session    include     system-auth
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/password/Makefile.am
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/password/Makefile.am	2009-03-04 21:03:53.177442234 -0500
@@ -0,0 +1,44 @@
+NULL =
+
+extensiondir = $(extensionsdatadir)/password
+extension_DATA = page.ui
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/common				\
+	-I$(top_srcdir)/gui/simple-greeter/libnotificationarea	\
+	-I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter	\
+	-DDMCONFDIR=\""$(dmconfdir)"\"			\
+	-DGDMCONFDIR=\"$(gdmconfdir)\"                  \
+	-DPLUGINDATADIR=\""$(extensiondir)"\"		\
+	-DPAMSERVICENAME=\""gdm"\"			\
+	-DSYSCONFDIR=\""$(sysconfdir)"\"		\
+	-DLIBLOCALEDIR=\""$(prefix)/lib/locale"\"	\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\" 	\
+	-DLIBEXECDIR=\""$(libexecdir)"\" 		\
+	-DSBINDIR=\""$(sbindir)"\"		 	\
+	$(DISABLE_DEPRECATED_CFLAGS)	\
+	$(GTK_CFLAGS)					\
+	$(SIMPLE_GREETER_CFLAGS)			\
+	$(POLKIT_GNOME_CFLAGS)				\
+	$(NULL)
+
+plugindir = $(GDM_SIMPLE_GREETER_PLUGINS_DIR)
+plugin_LTLIBRARIES = password.la
+
+password_la_CFLAGS =			\
+	$(SIMPLE_GREETER_CFLAGS)	\
+	$(NULL)
+
+password_la_LDFLAGS = -module -avoid-version -export-dynamic
+password_la_LIBADD = ../../../../common/libgdmcommon.la \
+			../../libgdmsimplegreeter/libgdmsimplegreeter.la
+password_la_SOURCES =				\
+			gdm-password-extension.h	\
+			gdm-password-extension.c	\
+			plugin.c
+
+EXTRA_DIST = $(extension_DATA)
+
+MAINTAINERCLEANFILES =                  \
+        *~                              \
+        Makefile.in
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/password/page.ui
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/password/page.ui	2009-03-04 21:03:53.178442358 -0500
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.14"/>
+    <object class="GtkVBox" id="page">
+      <property name="visible">True</property>
+      <property name="orientation">vertical</property>
+      <child>
+        <object class="GtkHBox" id="auth-input-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-prompt-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="expand">False</property>
+              <property name="fill">False</property>
+              <property name="position">0</property>
+            </packing>
+          </child>
+          <child>
+            <object class="GtkEntry" id="auth-prompt-entry">
+              <property name="visible">True</property>
+              <property name="can_focus">True</property>
+              <property name="activates_default">True</property>
+            </object>
+            <packing>
+              <property name="position">1</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">0</property>
+        </packing>
+      </child>
+      <child>
+        <object class="GtkHBox" id="auth-message-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-message-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="position">0</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">1</property>
+        </packing>
+      </child>
+    </object>
+</interface>
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/password/plugin.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/password/plugin.c	2009-03-04 21:03:53.179446254 -0500
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include "gdm-password-extension.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (void)
+{
+        static GObject *extension;
+
+        if (extension != NULL) {
+                g_object_ref (extension);
+        } else {
+                extension = g_object_new (GDM_TYPE_PASSWORD_EXTENSION, NULL);
+                g_object_add_weak_pointer (extension, (gpointer *) &extension);
+        }
+
+        return GDM_GREETER_EXTENSION (extension);
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard.c	2009-03-04 21:03:53.181445175 -0500
@@ -0,0 +1,558 @@
+/* gdm-smartcard.c - smartcard object
+ *
+ * Copyright (C) 2006 Ray Strode <rstrode@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#define GDM_SMARTCARD_ENABLE_INTERNAL_API
+#include "gdm-smartcard.h"
+
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <cert.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <prerror.h>
+#include <secmod.h>
+#include <secerr.h>
+
+struct _GdmSmartcardPrivate {
+        SECMODModule *module;
+        GdmSmartcardState state;
+
+        CK_SLOT_ID slot_id;
+        int slot_series;
+
+        PK11SlotInfo *slot;
+        char *name;
+
+        CERTCertificate *signing_certificate;
+        CERTCertificate *encryption_certificate;
+};
+
+static void gdm_smartcard_finalize (GObject *object);
+static void gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class);
+static void gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class);
+static void gdm_smartcard_set_property (GObject       *object,
+                                       guint          prop_id,
+                                       const GValue  *value,
+                                       GParamSpec    *pspec);
+static void gdm_smartcard_get_property (GObject     *object,
+                                       guint        prop_id,
+                                       GValue      *value,
+                                       GParamSpec  *pspec);
+static void gdm_smartcard_set_name (GdmSmartcard *card, const char *name);
+static void gdm_smartcard_set_slot_id (GdmSmartcard *card,
+                                      int                 slot_id);
+static void gdm_smartcard_set_slot_series (GdmSmartcard *card,
+                                          int          slot_series);
+static void gdm_smartcard_set_module (GdmSmartcard *card,
+                                     SECMODModule *module);
+
+static PK11SlotInfo *gdm_smartcard_find_slot_from_id (GdmSmartcard *card,
+                                                     int slot_id);
+
+static PK11SlotInfo *gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card,
+                                                            const char  *card_name);
+static gboolean gdm_smartcard_fetch_certificates (GdmSmartcard *card);
+
+#ifndef GDM_SMARTCARD_DEFAULT_SLOT_ID
+#define GDM_SMARTCARD_DEFAULT_SLOT_ID ((gulong) -1)
+#endif
+
+#ifndef GDM_SMARTCARD_DEFAULT_SLOT_SERIES
+#define GDM_SMARTCARD_DEFAULT_SLOT_SERIES -1
+#endif
+
+enum {
+        PROP_0 = 0,
+        PROP_NAME,
+        PROP_SLOT_ID,
+        PROP_SLOT_SERIES,
+        PROP_MODULE,
+        NUMBER_OF_PROPERTIES
+};
+
+enum {
+        INSERTED,
+        REMOVED,
+        NUMBER_OF_SIGNALS
+};
+
+static guint gdm_smartcard_signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE (GdmSmartcard, gdm_smartcard, G_TYPE_OBJECT);
+
+static void
+gdm_smartcard_class_init (GdmSmartcardClass *card_class)
+{
+        GObjectClass *gobject_class;
+
+        gobject_class = G_OBJECT_CLASS (card_class);
+
+        gobject_class->finalize = gdm_smartcard_finalize;
+
+        gdm_smartcard_class_install_signals (card_class);
+        gdm_smartcard_class_install_properties (card_class);
+
+        g_type_class_add_private (card_class,
+                                  sizeof (GdmSmartcardPrivate));
+}
+
+static void
+gdm_smartcard_class_install_signals (GdmSmartcardClass *card_class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (card_class);
+
+        gdm_smartcard_signals[INSERTED] =
+                g_signal_new ("inserted",
+                          G_OBJECT_CLASS_TYPE (object_class),
+                          G_SIGNAL_RUN_LAST,
+                          G_STRUCT_OFFSET (GdmSmartcardClass,
+                                           inserted),
+                          NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                          G_TYPE_NONE, 0);
+
+        gdm_smartcard_signals[REMOVED] =
+                g_signal_new ("removed",
+                          G_OBJECT_CLASS_TYPE (object_class),
+                          G_SIGNAL_RUN_LAST,
+                          G_STRUCT_OFFSET (GdmSmartcardClass,
+                                           removed),
+                          NULL, NULL, g_cclosure_marshal_VOID__VOID,
+                          G_TYPE_NONE, 0);
+}
+
+static void
+gdm_smartcard_class_install_properties (GdmSmartcardClass *card_class)
+{
+        GObjectClass *object_class;
+        GParamSpec *param_spec;
+
+        object_class = G_OBJECT_CLASS (card_class);
+        object_class->set_property = gdm_smartcard_set_property;
+        object_class->get_property = gdm_smartcard_get_property;
+
+        param_spec = g_param_spec_ulong ("slot-id", _("Slot ID"),
+                                   _("The slot the card is in"),
+                                   1, G_MAXULONG,
+                                   GDM_SMARTCARD_DEFAULT_SLOT_ID,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_SLOT_ID, param_spec);
+
+        param_spec = g_param_spec_int ("slot-series", _("Slot Series"),
+                                   _("per-slot card identifier"),
+                                   -1, G_MAXINT,
+                                   GDM_SMARTCARD_DEFAULT_SLOT_SERIES,
+                                   G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_SLOT_SERIES, param_spec);
+
+        param_spec = g_param_spec_string ("name", _("name"),
+                                      _("name"), NULL,
+                                      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_NAME, param_spec);
+
+        param_spec = g_param_spec_pointer ("module", _("Module"),
+                                       _("smartcard driver"),
+                                       G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_MODULE, param_spec);
+}
+
+static void
+gdm_smartcard_set_property (GObject       *object,
+                            guint          prop_id,
+                            const GValue  *value,
+                            GParamSpec    *pspec)
+{
+        GdmSmartcard *card = GDM_SMARTCARD (object);
+
+        switch (prop_id) {
+                case PROP_NAME:
+                        gdm_smartcard_set_name (card, g_value_get_string (value));
+                        break;
+
+                case PROP_SLOT_ID:
+                        gdm_smartcard_set_slot_id (card,
+                                                   g_value_get_ulong (value));
+                        break;
+
+                case PROP_SLOT_SERIES:
+                        gdm_smartcard_set_slot_series (card,
+                                                       g_value_get_int (value));
+                        break;
+
+                case PROP_MODULE:
+                        gdm_smartcard_set_module (card,
+                                                  (SECMODModule *)
+                                                  g_value_get_pointer (value));
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+}
+
+CK_SLOT_ID
+gdm_smartcard_get_slot_id (GdmSmartcard *card)
+{
+        return card->priv->slot_id;
+}
+
+GdmSmartcardState
+gdm_smartcard_get_state (GdmSmartcard *card)
+{
+        return card->priv->state;
+}
+
+char *
+gdm_smartcard_get_name (GdmSmartcard *card)
+{
+        return g_strdup (card->priv->name);
+}
+
+gboolean
+gdm_smartcard_is_login_card (GdmSmartcard *card)
+{
+        const char *login_card_name;
+        login_card_name = g_getenv ("PKCS11_LOGIN_TOKEN_NAME");
+
+        if ((login_card_name == NULL) || (card->priv->name == NULL)) {
+                return FALSE;
+        }
+
+        if (strcmp (card->priv->name, login_card_name) == 0) {
+                return TRUE;
+        }
+
+        return FALSE;
+}
+
+static void
+gdm_smartcard_get_property (GObject    *object,
+                            guint        prop_id,
+                            GValue      *value,
+                            GParamSpec  *pspec)
+{
+        GdmSmartcard *card = GDM_SMARTCARD (object);
+
+        switch (prop_id) {
+                case PROP_NAME:
+                        g_value_take_string (value,
+                                             gdm_smartcard_get_name (card));
+                        break;
+
+                case PROP_SLOT_ID:
+                        g_value_set_ulong (value,
+                                           (gulong) gdm_smartcard_get_slot_id (card));
+                        break;
+
+                case PROP_SLOT_SERIES:
+                        g_value_set_int (value,
+                                         gdm_smartcard_get_slot_series (card));
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        }
+}
+
+static void
+gdm_smartcard_set_name (GdmSmartcard *card,
+                        const char   *name)
+{
+        if (name == NULL) {
+                return;
+        }
+
+        if ((card->priv->name == NULL) ||
+            (strcmp (card->priv->name, name) != 0)) {
+                g_free (card->priv->name);
+                card->priv->name = g_strdup (name);
+
+                if (card->priv->slot == NULL) {
+                        card->priv->slot = gdm_smartcard_find_slot_from_card_name (card,
+                                                                                     card->priv->name);
+
+                        if (card->priv->slot != NULL) {
+                                int slot_id, slot_series;
+
+                                slot_id = PK11_GetSlotID (card->priv->slot);
+                                if (slot_id != card->priv->slot_id) {
+                                        gdm_smartcard_set_slot_id (card, slot_id);
+                                }
+
+                                slot_series = PK11_GetSlotSeries (card->priv->slot);
+                                if (slot_series != card->priv->slot_series) {
+                                        gdm_smartcard_set_slot_series (card, slot_series);
+                                }
+
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED);
+                        } else {
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED);
+                        }
+                }
+
+                g_object_notify (G_OBJECT (card), "name");
+        }
+}
+
+static void
+gdm_smartcard_set_slot_id (GdmSmartcard *card,
+                           int           slot_id)
+{
+        if (card->priv->slot_id != slot_id) {
+                card->priv->slot_id = slot_id;
+
+                if (card->priv->slot == NULL) {
+                        card->priv->slot = gdm_smartcard_find_slot_from_id (card,
+                                                                             card->priv->slot_id);
+
+                        if (card->priv->slot != NULL) {
+                                const char *card_name;
+
+                                card_name = PK11_GetTokenName (card->priv->slot);
+                                if ((card->priv->name == NULL) ||
+                                    ((card_name != NULL) &&
+                                    (strcmp (card_name, card->priv->name) != 0))) {
+                                        gdm_smartcard_set_name (card, card_name);
+                                }
+
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED);
+                        } else {
+                                _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED);
+                        }
+                }
+
+                g_object_notify (G_OBJECT (card), "slot-id");
+        }
+}
+
+static void
+gdm_smartcard_set_slot_series (GdmSmartcard *card,
+                               int           slot_series)
+{
+        if (card->priv->slot_series != slot_series) {
+                card->priv->slot_series = slot_series;
+                g_object_notify (G_OBJECT (card), "slot-series");
+        }
+}
+
+static void
+gdm_smartcard_set_module (GdmSmartcard *card,
+                          SECMODModule *module)
+{
+        gboolean should_notify;
+
+        if (card->priv->module != module) {
+                should_notify = TRUE;
+        } else {
+                should_notify = FALSE;
+        }
+
+        if (card->priv->module != NULL) {
+                SECMOD_DestroyModule (card->priv->module);
+                card->priv->module = NULL;
+        }
+
+        if (module != NULL) {
+                card->priv->module = SECMOD_ReferenceModule (module);
+        }
+
+        if (should_notify) {
+                g_object_notify (G_OBJECT (card), "module");
+        }
+}
+
+int
+gdm_smartcard_get_slot_series (GdmSmartcard *card)
+{
+        return card->priv->slot_series;
+}
+
+static void
+gdm_smartcard_init (GdmSmartcard *card)
+{
+
+        g_debug ("initializing smartcard ");
+
+        card->priv = G_TYPE_INSTANCE_GET_PRIVATE (card,
+                                                  GDM_TYPE_SMARTCARD,
+                                                  GdmSmartcardPrivate);
+
+        if (card->priv->slot != NULL) {
+                card->priv->name = g_strdup (PK11_GetTokenName (card->priv->slot));
+        }
+}
+
+static void gdm_smartcard_finalize (GObject *object)
+{
+        GdmSmartcard *card;
+        GObjectClass *gobject_class;
+
+        card = GDM_SMARTCARD (object);
+
+        g_free (card->priv->name);
+
+        gdm_smartcard_set_module (card, NULL);
+
+        gobject_class = G_OBJECT_CLASS (gdm_smartcard_parent_class);
+
+        gobject_class->finalize (object);
+}
+
+GQuark gdm_smartcard_error_quark (void)
+{
+        static GQuark error_quark = 0;
+
+        if (error_quark == 0) {
+                error_quark = g_quark_from_static_string ("gdm-smartcard-error-quark");
+        }
+
+        return error_quark;
+}
+
+GdmSmartcard *
+_gdm_smartcard_new (SECMODModule *module,
+                    CK_SLOT_ID    slot_id,
+                    int           slot_series)
+{
+        GdmSmartcard *card;
+
+        g_return_val_if_fail (module != NULL, NULL);
+        g_return_val_if_fail (slot_id >= 1, NULL);
+        g_return_val_if_fail (slot_series > 0, NULL);
+        g_return_val_if_fail (sizeof (gulong) == sizeof (slot_id), NULL);
+
+        card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD,
+                                             "module", module,
+                                             "slot-id", (gulong) slot_id,
+                                             "slot-series", slot_series,
+                                             NULL));
+        return card;
+}
+
+GdmSmartcard *
+_gdm_smartcard_new_from_name (SECMODModule *module,
+                              const char   *name)
+{
+        GdmSmartcard *card;
+
+        g_return_val_if_fail (module != NULL, NULL);
+        g_return_val_if_fail (name != NULL, NULL);
+
+        card = GDM_SMARTCARD (g_object_new (GDM_TYPE_SMARTCARD,
+                                            "module", module,
+                                            "name", name,
+                                            NULL));
+        return card;
+}
+
+void
+_gdm_smartcard_set_state (GdmSmartcard      *card,
+                          GdmSmartcardState  state)
+{
+        /* gdm_smartcard_fetch_certificates (card); */
+        if (card->priv->state != state) {
+                card->priv->state = state;
+
+                if (state == GDM_SMARTCARD_STATE_INSERTED) {
+                        g_signal_emit (card, gdm_smartcard_signals[INSERTED], 0);
+                } else if (state == GDM_SMARTCARD_STATE_REMOVED) {
+                        g_signal_emit (card, gdm_smartcard_signals[REMOVED], 0);
+                } else {
+                        g_assert_not_reached ();
+                }
+        }
+}
+
+/* So we could conceivably make the closure data a pointer to the card
+ * or something similiar and then emit signals when we want passwords,
+ * but it's probably easier to just get the password up front and use
+ * it.  So we just take the passed in g_malloc'd (well probably, who knows)
+ * and strdup it using NSPR's memory allocation routines.
+ */
+static char *
+gdm_smartcard_password_handler (PK11SlotInfo *slot,
+                                PRBool        is_retrying,
+                                const char   *password)
+{
+        if (is_retrying) {
+                return NULL;
+        }
+
+        return password != NULL? PL_strdup (password): NULL;
+}
+
+gboolean
+gdm_smartcard_unlock (GdmSmartcard *card,
+                      const char   *password)
+{
+        SECStatus status;
+
+        PK11_SetPasswordFunc ((PK11PasswordFunc) gdm_smartcard_password_handler);
+
+        /* we pass PR_TRUE to load certificates
+        */
+        status = PK11_Authenticate (card->priv->slot, PR_TRUE, (gpointer) password);
+
+        if (status != SECSuccess) {
+                g_debug ("could not unlock card - %d", status);
+                return FALSE;
+        }
+        return TRUE;
+}
+
+static PK11SlotInfo *
+gdm_smartcard_find_slot_from_card_name (GdmSmartcard *card,
+                                        const char   *card_name)
+{
+        int i;
+
+        for (i = 0; i < card->priv->module->slotCount; i++) {
+                const char *slot_card_name;
+
+                slot_card_name = PK11_GetTokenName (card->priv->module->slots[i]);
+
+                if ((slot_card_name != NULL) &&
+                    (strcmp (slot_card_name, card_name) == 0)) {
+                        return card->priv->module->slots[i];
+                }
+        }
+
+        return NULL;
+}
+
+static PK11SlotInfo *
+gdm_smartcard_find_slot_from_id (GdmSmartcard *card,
+                                 int           slot_id)
+{
+        int i;
+
+        for (i = 0; i < card->priv->module->slotCount; i++) {
+                if (PK11_GetSlotID (card->priv->module->slots[i]) == slot_id) {
+                        return card->priv->module->slots[i];
+                }
+        }
+
+        return NULL;
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c	2009-03-04 21:03:53.182438804 -0500
@@ -0,0 +1,440 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include <config.h>
+#include "gdm-smartcard-extension.h"
+#include "gdm-conversation.h"
+#include "gdm-task.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include <glib/gi18n-lib.h>
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+#ifndef GDM_SMARTCARD_WORKER_COMMAND
+#define GDM_SMARTCARD_WORKER_COMMAND LIBEXECDIR "/gdm-smartcard-worker"
+#endif
+
+struct _GdmSmartcardExtensionPrivate
+{
+        GIcon     *icon;
+        GtkWidget *page;
+        GtkActionGroup *actions;
+        GtkAction  *login_action;
+
+        GtkWidget *message_label;
+        GtkWidget *prompt_label;
+        GtkWidget *prompt_entry;
+
+        GPid       worker_pid;
+        int        number_of_tokens;
+
+        guint      answer_pending : 1;
+        guint      select_when_ready : 1;
+};
+
+static void gdm_smartcard_extension_finalize (GObject *object);
+
+static void gdm_task_iface_init (GdmTaskIface *iface);
+static void gdm_conversation_iface_init (GdmConversationIface *iface);
+static void gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface);
+
+G_DEFINE_TYPE_WITH_CODE (GdmSmartcardExtension,
+                         gdm_smartcard_extension,
+                         G_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_GREETER_EXTENSION,
+                                                gdm_greeter_extension_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_TASK,
+                                                gdm_task_iface_init)
+                         G_IMPLEMENT_INTERFACE (GDM_TYPE_CONVERSATION,
+                                                gdm_conversation_iface_init));
+
+static gboolean
+on_smartcard_event (GIOChannel   *io_channel,
+                    GIOCondition  condition,
+                    gpointer      data)
+{
+        GdmSmartcardExtension *extension;
+
+        extension = GDM_SMARTCARD_EXTENSION (data);
+
+        if (condition & G_IO_IN) {
+                char buffer[1024];
+                ssize_t num_bytes;
+
+                num_bytes = read (g_io_channel_unix_get_fd (io_channel),
+                                  buffer, sizeof (buffer));
+
+                if (num_bytes < 0 && errno != EINTR)
+                        return FALSE;
+
+                if (num_bytes != 1) {
+                        g_debug ("buffer: %s\n", buffer);
+                        return TRUE;
+                }
+
+                if (buffer[0] == 'I') {
+                        extension->priv->number_of_tokens++;
+                } else {
+                        extension->priv->number_of_tokens--;
+                }
+
+                if (extension->priv->number_of_tokens == 1) {
+                        if (!gdm_conversation_choose_user (GDM_CONVERSATION (extension),
+                                                          PAMSERVICENAME)) {
+                                g_debug ("could not choose smart card user, cancelling...");
+                                gdm_conversation_cancel (GDM_CONVERSATION (extension));
+                                extension->priv->select_when_ready = TRUE;
+                        } else {
+                                g_debug ("chose smart card user!");
+                        }
+                } else if (extension->priv->number_of_tokens == 0) {
+                        gdm_conversation_cancel (GDM_CONVERSATION (extension));
+                }
+
+                return TRUE;
+        }
+
+        if (condition & G_IO_HUP) {
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static void
+watch_for_smartcards (GdmSmartcardExtension *extension)
+{
+        GError *error;
+        GIOChannel *io_channel;
+        char *args[] = { GDM_SMARTCARD_WORKER_COMMAND, NULL };
+        GPid pid;
+        int stdout_fd;
+
+        error = NULL;
+
+        if (!g_spawn_async_with_pipes (NULL, args, NULL, 0,
+                                       NULL, NULL, &pid, NULL,
+                                       &stdout_fd, NULL, &error)) {
+                g_debug ("could not start smart card manager: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+        fcntl (stdout_fd, F_SETFD, FD_CLOEXEC);
+
+        io_channel = g_io_channel_unix_new (stdout_fd);
+        g_io_channel_set_flags (io_channel, G_IO_FLAG_NONBLOCK, NULL);
+        g_io_channel_set_encoding (io_channel, NULL, NULL);
+        g_io_channel_set_buffered (io_channel, FALSE);
+        g_io_add_watch (io_channel, G_IO_IN, on_smartcard_event, extension);
+        g_io_channel_set_close_on_unref (io_channel, TRUE);
+        g_io_channel_unref (io_channel);
+
+        extension->priv->worker_pid = pid;
+}
+
+static void
+gdm_smartcard_extension_set_message (GdmConversation *conversation,
+                                       const char *message)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->message_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->message_label), message);
+}
+
+static void
+gdm_smartcard_extension_ask_question (GdmConversation *conversation,
+                                        const char      *message)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_action_set_visible (extension->priv->login_action, TRUE);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_smartcard_extension_ask_secret (GdmConversation *conversation,
+                                      const char      *message)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+        gtk_widget_show (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), message);
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), FALSE);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_widget_show (extension->priv->prompt_entry);
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        gtk_action_set_visible (extension->priv->login_action, TRUE);
+        extension->priv->answer_pending = TRUE;
+}
+
+static void
+gdm_smartcard_extension_reset (GdmConversation *conversation)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+        gtk_widget_hide (extension->priv->prompt_label);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_entry_set_visibility (GTK_ENTRY (extension->priv->prompt_entry), TRUE);
+        gtk_action_set_visible (extension->priv->login_action, FALSE);
+        extension->priv->answer_pending = FALSE;
+
+        gdm_task_set_enabled (GDM_TASK (conversation), FALSE);
+}
+
+static void
+gdm_smartcard_extension_set_ready (GdmConversation *conversation)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+        gdm_task_set_enabled (GDM_TASK (conversation), TRUE);
+
+        if (extension->priv->worker_pid <= 0) {
+                watch_for_smartcards (extension);
+        }
+
+        if (extension->priv->select_when_ready) {
+                if (gdm_conversation_choose_user (GDM_CONVERSATION (extension),
+                                                  PAMSERVICENAME)) {
+                        extension->priv->select_when_ready = FALSE;
+                }
+        }
+}
+
+char *
+gdm_smartcard_extension_get_service_name (GdmConversation *conversation)
+{
+        return g_strdup (PAMSERVICENAME);
+}
+
+GtkWidget *
+gdm_smartcard_extension_get_page (GdmConversation *conversation)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+        return extension->priv->page;
+}
+
+GtkActionGroup *
+gdm_smartcard_extension_get_actions (GdmConversation *conversation)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+
+        return g_object_ref (extension->priv->actions);
+}
+
+void
+gdm_smartcard_extension_request_answer (GdmConversation *conversation)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+        const char *text;
+
+        if (!extension->priv->answer_pending) {
+                gdm_conversation_answer (conversation, NULL);
+                return;
+        }
+
+        extension->priv->answer_pending = FALSE;
+        text = gtk_entry_get_text (GTK_ENTRY (extension->priv->prompt_entry));
+        gdm_conversation_answer (conversation, text);
+
+        gtk_widget_hide (extension->priv->prompt_entry);
+        gtk_label_set_text (GTK_LABEL (extension->priv->prompt_label), "");
+        gtk_entry_set_text (GTK_ENTRY (extension->priv->prompt_entry), "");
+        gtk_action_set_visible (extension->priv->login_action, FALSE);
+}
+
+gboolean
+gdm_smartcard_extension_focus (GdmConversation *conversation)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (conversation);
+
+        if (!extension->priv->answer_pending) {
+                return FALSE;
+        }
+
+        gtk_widget_grab_focus (extension->priv->prompt_entry);
+        return TRUE;
+}
+
+GIcon *
+gdm_smartcard_extension_get_icon (GdmTask *task)
+{
+        GdmSmartcardExtension *extension = GDM_SMARTCARD_EXTENSION (task);
+        return g_object_ref (extension->priv->icon);
+}
+
+char *
+gdm_smartcard_extension_get_name (GdmTask *task)
+{
+        return g_strdup (_("Smartcard Authentication"));
+}
+
+char *
+gdm_smartcard_extension_get_description (GdmTask *task)
+{
+        return g_strdup (_("Log into session with smartcard"));
+}
+
+gboolean
+gdm_smartcard_extension_is_choosable (GdmTask *task)
+{
+        return TRUE;
+}
+
+static void
+gdm_task_iface_init (GdmTaskIface *iface)
+{
+        iface->get_icon = gdm_smartcard_extension_get_icon;
+        iface->get_description = gdm_smartcard_extension_get_description;
+        iface->get_name = gdm_smartcard_extension_get_name;
+        iface->is_choosable = gdm_smartcard_extension_is_choosable;
+}
+
+static void
+gdm_conversation_iface_init (GdmConversationIface *iface)
+{
+        iface->set_message = gdm_smartcard_extension_set_message;
+        iface->ask_question = gdm_smartcard_extension_ask_question;
+        iface->ask_secret = gdm_smartcard_extension_ask_secret;
+        iface->reset = gdm_smartcard_extension_reset;
+        iface->set_ready = gdm_smartcard_extension_set_ready;
+        iface->get_service_name = gdm_smartcard_extension_get_service_name;
+        iface->get_page = gdm_smartcard_extension_get_page;
+        iface->get_actions = gdm_smartcard_extension_get_actions;
+        iface->request_answer = gdm_smartcard_extension_request_answer;
+        iface->focus = gdm_smartcard_extension_focus;
+}
+
+static void
+gdm_greeter_extension_iface_init (GdmGreeterExtensionIface *iface)
+{
+}
+
+static void
+gdm_smartcard_extension_class_init (GdmSmartcardExtensionClass *extension_class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (extension_class);
+
+        object_class->finalize = gdm_smartcard_extension_finalize;
+
+        g_type_class_add_private (extension_class,
+                                  sizeof (GdmSmartcardExtensionPrivate));
+}
+
+static void
+gdm_smartcard_extension_finalize (GObject *object)
+{
+}
+
+static void
+create_page (GdmSmartcardExtension *extension)
+{
+        GtkBuilder *builder;
+        GObject *object;
+        GError *error;
+
+        builder = gtk_builder_new ();
+
+        error = NULL;
+        gtk_builder_add_from_file (builder,
+                                   PLUGINDATADIR "/page.ui",
+                                   &error);
+
+        if (error != NULL) {
+                g_warning ("Could not load UI file: %s", error->message);
+                g_error_free (error);
+                return;
+        }
+
+        object = gtk_builder_get_object (builder, "page");
+        g_object_ref (object);
+
+        extension->priv->page = GTK_WIDGET (object);
+
+        object = gtk_builder_get_object (builder, "auth-prompt-label");
+        g_object_ref (object);
+        extension->priv->prompt_label = GTK_WIDGET (object);
+        gtk_widget_hide (extension->priv->prompt_label);
+
+        object = gtk_builder_get_object (builder, "auth-prompt-entry");
+        g_object_ref (object);
+        extension->priv->prompt_entry = GTK_WIDGET (object);
+        gtk_widget_hide (extension->priv->prompt_entry);
+
+        object = gtk_builder_get_object (builder, "auth-message-label");
+        g_object_ref (object);
+        extension->priv->message_label = GTK_WIDGET (object);
+        gtk_widget_show (extension->priv->message_label);
+
+        g_object_unref (builder);
+}
+
+static void
+on_activate_log_in (GdmSmartcardExtension *extension)
+{
+        gdm_smartcard_extension_request_answer (GDM_CONVERSATION (extension));
+}
+
+static void
+create_actions (GdmSmartcardExtension *extension)
+{
+        GtkAction *action;
+
+        extension->priv->actions = gtk_action_group_new ("gdm-smartcard-extension");
+
+        action = gtk_action_new (GDM_CONVERSATION_DEFAULT_ACTION,
+                                 _("Log In"),
+                                 _("Log into the currently selected sesson"),
+                                 NULL);
+        g_signal_connect_swapped (action, "activate",
+                                  G_CALLBACK (on_activate_log_in), extension);
+        g_object_set (G_OBJECT (action), "icon-name", "go-home", NULL);
+        gtk_action_group_add_action (extension->priv->actions,
+                                     action);
+
+        gtk_action_set_visible (action, FALSE);
+        extension->priv->login_action = action;
+}
+
+static void
+gdm_smartcard_extension_init (GdmSmartcardExtension *extension)
+{
+        extension->priv = G_TYPE_INSTANCE_GET_PRIVATE (extension,
+                                                       GDM_TYPE_SMARTCARD_EXTENSION,
+                                                       GdmSmartcardExtensionPrivate);
+
+        extension->priv->icon = g_themed_icon_new ("apple-green");
+        create_page (extension);
+        create_actions (extension);
+        gdm_smartcard_extension_reset (GDM_CONVERSATION (extension));
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h	2009-03-04 21:03:53.184436468 -0500
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ */
+
+#ifndef __GDM_SMARTCARD_EXTENSION_H
+#define __GDM_SMARTCARD_EXTENSION_H
+
+#include <glib-object.h>
+#include "gdm-greeter-extension.h"
+
+G_BEGIN_DECLS
+
+#define GDM_TYPE_SMARTCARD_EXTENSION (gdm_smartcard_extension_get_type ())
+#define GDM_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtension))
+#define GDM_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass))
+#define GDM_IS_SMARTCARD_EXTENSION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD_EXTENSION))
+#define GDM_IS_SMARTCARD_EXTENSION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD_EXTENSION))
+#define GDM_SMARTCARD_EXTENSION_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_EXTENSION, GdmSmartcardExtensionClass))
+
+typedef struct _GdmSmartcardExtensionPrivate GdmSmartcardExtensionPrivate;
+
+typedef struct
+{
+        GObject                  parent;
+        GdmSmartcardExtensionPrivate *priv;
+} GdmSmartcardExtension;
+
+typedef struct
+{
+        GObjectClass parent_class;
+} GdmSmartcardExtensionClass;
+
+GType                 gdm_smartcard_extension_get_type      (void);
+
+GdmSmartcardExtension *gdm_smartcard_extension_new       (void);
+
+G_END_DECLS
+
+#endif /* GDM_SMARTCARD_EXTENSION_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard.h	2009-03-04 21:03:53.185432192 -0500
@@ -0,0 +1,94 @@
+/* securitycard.h - api for reading and writing data to a security card
+ *
+ * Copyright (C) 2006 Ray Strode
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ */
+#ifndef GDM_SMARTCARD_H
+#define GDM_SMARTCARD_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <secmod.h>
+
+G_BEGIN_DECLS
+#define GDM_TYPE_SMARTCARD            (gdm_smartcard_get_type ())
+#define GDM_SMARTCARD(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD, GdmSmartcard))
+#define GDM_SMARTCARD_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD, GdmSmartcardClass))
+#define GDM_IS_SMARTCARD(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GDM_TYPE_SMARTCARD))
+#define GDM_IS_SMARTCARD_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), GDM_TYPE_SMARTCARD))
+#define GDM_SMARTCARD_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD, GdmSmartcardClass))
+#define GDM_SMARTCARD_ERROR           (gdm_smartcard_error_quark ())
+typedef struct _GdmSmartcardClass GdmSmartcardClass;
+typedef struct _GdmSmartcard GdmSmartcard;
+typedef struct _GdmSmartcardPrivate GdmSmartcardPrivate;
+typedef enum _GdmSmartcardError GdmSmartcardError;
+typedef enum _GdmSmartcardState GdmSmartcardState;
+
+typedef struct _GdmSmartcardRequest GdmSmartcardRequest;
+
+struct _GdmSmartcard {
+    GObject parent;
+
+    /*< private > */
+    GdmSmartcardPrivate *priv;
+};
+
+struct _GdmSmartcardClass {
+    GObjectClass parent_class;
+
+    void (* inserted) (GdmSmartcard *card);
+    void (* removed)  (GdmSmartcard *card);
+};
+
+enum _GdmSmartcardError {
+    GDM_SMARTCARD_ERROR_GENERIC = 0,
+};
+
+enum _GdmSmartcardState {
+    GDM_SMARTCARD_STATE_INSERTED = 0,
+    GDM_SMARTCARD_STATE_REMOVED,
+};
+
+GType gdm_smartcard_get_type (void) G_GNUC_CONST;
+GQuark gdm_smartcard_error_quark (void) G_GNUC_CONST;
+
+CK_SLOT_ID gdm_smartcard_get_slot_id (GdmSmartcard *card);
+gint gdm_smartcard_get_slot_series (GdmSmartcard *card);
+GdmSmartcardState gdm_smartcard_get_state (GdmSmartcard *card);
+
+char *gdm_smartcard_get_name (GdmSmartcard *card);
+gboolean gdm_smartcard_is_login_card (GdmSmartcard *card);
+
+gboolean gdm_smartcard_unlock (GdmSmartcard *card,
+                               const char   *password);
+
+/* don't under any circumstances call these functions */
+#ifdef GDM_SMARTCARD_ENABLE_INTERNAL_API
+
+GdmSmartcard *_gdm_smartcard_new (SECMODModule *module,
+                                  CK_SLOT_ID    slot_id,
+                                  gint          slot_series);
+GdmSmartcard *_gdm_smartcard_new_from_name (SECMODModule *module,
+                                            const char   *name);
+
+void _gdm_smartcard_set_state (GdmSmartcard      *card,
+                               GdmSmartcardState  state);
+#endif
+
+G_END_DECLS
+#endif                                /* GDM_SMARTCARD_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c	2009-03-04 21:03:53.188432285 -0500
@@ -0,0 +1,1394 @@
+/* gdm-smartcard-manager.c - object for monitoring smartcard insertion and
+ *                           removal events
+ *
+ * Copyright (C) 2006, 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written By: Ray Strode
+ */
+#define _GNU_SOURCE
+#include "gdm-smartcard-manager.h"
+
+#define GDM_SMARTCARD_ENABLE_INTERNAL_API
+#include "gdm-smartcard.h"
+
+#include <dirent.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <poll.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include <prerror.h>
+#include <nss.h>
+#include <pk11func.h>
+#include <secmod.h>
+#include <secerr.h>
+
+#ifndef GDM_SMARTCARD_MANAGER_DRIVER
+#define GDM_SMARTCARD_MANAGER_DRIVER LIBDIR"/pkcs11/libcoolkeypk11.so"
+#endif
+
+#ifndef GDM_SMARTCARD_MANAGER_NSS_DB
+#define GDM_SMARTCARD_MANAGER_NSS_DB SYSCONFDIR"/pki/nssdb"
+#endif
+
+#ifndef GDM_MAX_OPEN_FILE_DESCRIPTORS
+#define GDM_MAX_OPEN_FILE_DESCRIPTORS 1024
+#endif
+
+#ifndef GDM_OPEN_FILE_DESCRIPTORS_DIR
+#define GDM_OPEN_FILE_DESCRIPTORS_DIR "/proc/self/fd"
+#endif
+
+typedef enum _GdmSmartcardManagerState GdmSmartcardManagerState;
+typedef struct _GdmSmartcardManagerWorker GdmSmartcardManagerWorker;
+
+enum _GdmSmartcardManagerState {
+        GDM_SMARTCARD_MANAGER_STATE_STOPPED = 0,
+        GDM_SMARTCARD_MANAGER_STATE_STARTING,
+        GDM_SMARTCARD_MANAGER_STATE_STARTED,
+        GDM_SMARTCARD_MANAGER_STATE_STOPPING,
+};
+
+struct _GdmSmartcardManagerPrivate {
+        GdmSmartcardManagerState state;
+        SECMODModule *module;
+        char        *module_path;
+
+        GSource *smartcard_event_source;
+        GPid smartcard_event_watcher_pid;
+        GHashTable *smartcards;
+
+        GThread    *worker_thread;
+
+        guint poll_timeout_id;
+
+        guint32 is_unstoppable : 1;
+        guint32 nss_is_loaded : 1;
+};
+
+struct _GdmSmartcardManagerWorker {
+        SECMODModule *module;
+        GHashTable *smartcards;
+        gint write_fd;
+
+        guint32 nss_is_loaded : 1;
+};
+
+static void gdm_smartcard_manager_finalize (GObject *object);
+static void gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *service_class);
+static void gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *service_class);
+static void gdm_smartcard_manager_set_property (GObject       *object,
+                                                guint          prop_id,
+                                                const GValue  *value,
+                                                GParamSpec    *pspec);
+static void gdm_smartcard_manager_get_property (GObject    *object,
+                                                guint       prop_id,
+                                                GValue     *value,
+                                                GParamSpec *pspec);
+static void gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager,
+                                                   const char          *module_path);
+static void gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager,
+                                                        GdmSmartcard        *card);
+static void gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager_class,
+                                                         GdmSmartcard        *card);
+static gboolean gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager);
+static void gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager);
+
+static gboolean gdm_smartcard_manager_create_worker (GdmSmartcardManager *manager,
+                                                     int *worker_fd, GThread **worker_thread);
+
+static GdmSmartcardManagerWorker * gdm_smartcard_manager_worker_new (gint write_fd);
+static void gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker);
+static gboolean gdm_open_pipe (gint *write_fd, gint *read_fd);
+static gboolean sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes);
+static gboolean sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes);
+static GdmSmartcard *sc_read_smartcard (gint fd, SECMODModule *module);
+static gboolean sc_write_smartcard (gint fd, GdmSmartcard *card);
+
+enum {
+        PROP_0 = 0,
+        PROP_MODULE_PATH,
+        NUMBER_OF_PROPERTIES
+};
+
+enum {
+        SMARTCARD_INSERTED = 0,
+        SMARTCARD_REMOVED,
+        ERROR,
+        NUMBER_OF_SIGNALS
+};
+
+static guint gdm_smartcard_manager_signals[NUMBER_OF_SIGNALS];
+
+G_DEFINE_TYPE (GdmSmartcardManager,
+               gdm_smartcard_manager,
+               G_TYPE_OBJECT);
+
+static void
+gdm_smartcard_manager_class_init (GdmSmartcardManagerClass *manager_class)
+{
+        GObjectClass *gobject_class;
+
+        gobject_class = G_OBJECT_CLASS (manager_class);
+
+        gobject_class->finalize = gdm_smartcard_manager_finalize;
+
+        gdm_smartcard_manager_class_install_signals (manager_class);
+        gdm_smartcard_manager_class_install_properties (manager_class);
+
+        g_type_class_add_private (manager_class,
+                                  sizeof (GdmSmartcardManagerPrivate));
+}
+
+static void
+gdm_smartcard_manager_class_install_properties (GdmSmartcardManagerClass *card_class)
+{
+        GObjectClass *object_class;
+        GParamSpec *param_spec;
+
+        object_class = G_OBJECT_CLASS (card_class);
+        object_class->set_property = gdm_smartcard_manager_set_property;
+        object_class->get_property = gdm_smartcard_manager_get_property;
+
+        param_spec = g_param_spec_string ("module-path", _("Module Path"),
+                                          _("path to smartcard PKCS #11 driver"),
+                                          NULL, G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
+        g_object_class_install_property (object_class, PROP_MODULE_PATH, param_spec);
+}
+
+static void
+gdm_smartcard_manager_set_property (GObject       *object,
+                                    guint          prop_id,
+                                    const GValue  *value,
+                                    GParamSpec    *pspec)
+{
+        GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object);
+
+        switch (prop_id) {
+                case PROP_MODULE_PATH:
+                        gdm_smartcard_manager_set_module_path (manager,
+                                                                   g_value_get_string (value));
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                        break;
+        }
+}
+
+static void
+gdm_smartcard_manager_get_property (GObject    *object,
+                                    guint       prop_id,
+                                    GValue     *value,
+                                    GParamSpec *pspec)
+{
+        GdmSmartcardManager *manager = GDM_SMARTCARD_MANAGER (object);
+        char *module_path;
+
+        switch (prop_id) {
+                case PROP_MODULE_PATH:
+                        module_path = gdm_smartcard_manager_get_module_path (manager);
+                        g_value_set_string (value, module_path);
+                        g_free (module_path);
+                        break;
+
+                default:
+                        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+                        break;
+        }
+}
+
+char *
+gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager)
+{
+        return manager->priv->module_path;
+}
+
+static void
+gdm_smartcard_manager_set_module_path (GdmSmartcardManager *manager,
+                                       const char          *module_path)
+{
+        if ((manager->priv->module_path == NULL) && (module_path == NULL)) {
+                return;
+        }
+
+        if (((manager->priv->module_path == NULL) ||
+         (module_path == NULL) ||
+         (strcmp (manager->priv->module_path, module_path) != 0))) {
+                g_free (manager->priv->module_path);
+                manager->priv->module_path = g_strdup (module_path);
+                g_object_notify (G_OBJECT (manager), "module-path");
+        }
+}
+
+static void
+gdm_smartcard_manager_card_removed_handler (GdmSmartcardManager *manager,
+                                            GdmSmartcard        *card)
+{
+        g_debug ("informing smartcard of its removal");
+        _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_REMOVED);
+        g_debug ("done");
+}
+
+static void
+gdm_smartcard_manager_card_inserted_handler (GdmSmartcardManager *manager,
+                                             GdmSmartcard        *card)
+{
+        g_debug ("informing smartcard of its insertion");
+
+        _gdm_smartcard_set_state (card, GDM_SMARTCARD_STATE_INSERTED);
+        g_debug ("done");
+
+}
+
+static void
+gdm_smartcard_manager_class_install_signals (GdmSmartcardManagerClass *manager_class)
+{
+        GObjectClass *object_class;
+
+        object_class = G_OBJECT_CLASS (manager_class);
+
+        gdm_smartcard_manager_signals[SMARTCARD_INSERTED] =
+                g_signal_new ("smartcard-inserted",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSmartcardManagerClass,
+                                               smartcard_inserted),
+                              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+        manager_class->smartcard_inserted = gdm_smartcard_manager_card_inserted_handler;
+
+        gdm_smartcard_manager_signals[SMARTCARD_REMOVED] =
+                g_signal_new ("smartcard-removed",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSmartcardManagerClass,
+                                               smartcard_removed),
+                              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+        manager_class->smartcard_removed = gdm_smartcard_manager_card_removed_handler;
+
+        gdm_smartcard_manager_signals[ERROR] =
+                g_signal_new ("error",
+                              G_OBJECT_CLASS_TYPE (object_class),
+                              G_SIGNAL_RUN_LAST,
+                              G_STRUCT_OFFSET (GdmSmartcardManagerClass, error),
+                              NULL, NULL, g_cclosure_marshal_VOID__POINTER,
+                              G_TYPE_NONE, 1, G_TYPE_POINTER);
+        manager_class->error = NULL;
+}
+
+static gboolean
+sc_slot_id_equal (CK_SLOT_ID *slot_id_1,
+                      CK_SLOT_ID *slot_id_2)
+{
+        g_assert (slot_id_1 != NULL);
+        g_assert (slot_id_2 != NULL);
+
+        return *slot_id_1 == *slot_id_2;
+}
+
+static gboolean
+sc_slot_id_hash (CK_SLOT_ID *slot_id)
+{
+        guint32 upper_bits, lower_bits;
+        gint temp;
+
+        if (sizeof (CK_SLOT_ID) == sizeof (gint)) {
+                return g_int_hash (slot_id);
+        }
+
+        upper_bits = ((*slot_id) >> 31) - 1;
+        lower_bits = (*slot_id) & 0xffffffff;
+
+        /* The upper bits are almost certainly always zero,
+         * so let's degenerate to g_int_hash for the
+         * (very) common case
+         */
+        temp = lower_bits + upper_bits;
+        return upper_bits + g_int_hash (&temp);
+}
+
+static void
+gdm_smartcard_manager_init (GdmSmartcardManager *manager)
+{
+        g_debug ("initializing smartcard manager");
+
+        manager->priv = G_TYPE_INSTANCE_GET_PRIVATE (manager,
+                                                     GDM_TYPE_SMARTCARD_MANAGER,
+                                                     GdmSmartcardManagerPrivate);
+        manager->priv->poll_timeout_id = 0;
+        manager->priv->is_unstoppable = FALSE;
+        manager->priv->module = NULL;
+
+        manager->priv->smartcards =
+                g_hash_table_new_full (g_str_hash,
+                                       g_str_equal,
+                                       (GDestroyNotify) g_free,
+                                       (GDestroyNotify) g_object_unref);
+
+        if (!g_thread_supported()) {
+        g_thread_init (NULL);
+        }
+
+}
+
+static void
+gdm_smartcard_manager_finalize (GObject *object)
+{
+        GdmSmartcardManager *manager;
+        GObjectClass *gobject_class;
+
+        manager = GDM_SMARTCARD_MANAGER (object);
+        gobject_class =
+                G_OBJECT_CLASS (gdm_smartcard_manager_parent_class);
+
+        gdm_smartcard_manager_stop_now (manager);
+
+        g_hash_table_destroy (manager->priv->smartcards);
+        manager->priv->smartcards = NULL;
+
+        gobject_class->finalize (object);
+}
+
+GQuark
+gdm_smartcard_manager_error_quark (void)
+{
+        static GQuark error_quark = 0;
+
+        if (error_quark == 0) {
+                error_quark = g_quark_from_static_string ("gdm-smartcard-manager-error-quark");
+        }
+
+        return error_quark;
+}
+
+GdmSmartcardManager *
+gdm_smartcard_manager_new (const char *module_path)
+{
+        GdmSmartcardManager *instance;
+
+        instance = GDM_SMARTCARD_MANAGER (g_object_new (GDM_TYPE_SMARTCARD_MANAGER,
+                                                        "module-path", module_path,
+                                                        NULL));
+
+        return instance;
+}
+
+static void
+gdm_smartcard_manager_emit_error (GdmSmartcardManager *manager,
+                                  GError              *error)
+{
+        manager->priv->is_unstoppable = TRUE;
+        g_signal_emit (manager, gdm_smartcard_manager_signals[ERROR], 0,
+                       error);
+        manager->priv->is_unstoppable = FALSE;
+}
+
+static void
+gdm_smartcard_manager_emit_smartcard_inserted (GdmSmartcardManager *manager,
+                                               GdmSmartcard        *card)
+{
+        manager->priv->is_unstoppable = TRUE;
+        g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_INSERTED], 0,
+                       card);
+        manager->priv->is_unstoppable = FALSE;
+}
+
+static void
+gdm_smartcard_manager_emit_smartcard_removed (GdmSmartcardManager *manager,
+                                              GdmSmartcard        *card)
+{
+        GdmSmartcardManagerState old_state;
+
+        old_state = manager->priv->state;
+        manager->priv->is_unstoppable = TRUE;
+        g_signal_emit (manager, gdm_smartcard_manager_signals[SMARTCARD_REMOVED], 0,
+                       card);
+        manager->priv->is_unstoppable = FALSE;
+}
+
+static gboolean
+gdm_smartcard_manager_check_for_and_process_events (GIOChannel          *io_channel,
+                                                    GIOCondition         condition,
+                                                    GdmSmartcardManager *manager)
+{
+        GdmSmartcard *card;
+        gboolean should_stop;
+        guchar event_type;
+        char *card_name;
+        gint fd;
+
+        card = NULL;
+        should_stop = (condition & G_IO_HUP) || (condition & G_IO_ERR);
+
+        if (should_stop) {
+                g_debug ("received %s on event socket, stopping "
+                          "manager...",
+                          (condition & G_IO_HUP) && (condition & G_IO_ERR)?
+                          "error and hangup" :
+                          (condition & G_IO_HUP)?
+                          "hangup" : "error");
+        }
+
+        if (!(condition & G_IO_IN)) {
+                goto out;
+        }
+
+        fd = g_io_channel_unix_get_fd (io_channel);
+
+        event_type = '\0';
+        if (!sc_read_bytes (fd, &event_type, 1)) {
+                should_stop = TRUE;
+                goto out;
+        }
+
+        card = sc_read_smartcard (fd, manager->priv->module);
+
+        if (card == NULL) {
+                should_stop = TRUE;
+                goto out;
+        }
+
+        card_name = gdm_smartcard_get_name (card);
+
+        switch (event_type) {
+                case 'I':
+                        g_hash_table_replace (manager->priv->smartcards,
+                                              card_name, card);
+                        card_name = NULL;
+
+                        gdm_smartcard_manager_emit_smartcard_inserted (manager, card);
+                        card = NULL;
+                        break;
+
+                case 'R':
+                        gdm_smartcard_manager_emit_smartcard_removed (manager, card);
+                        if (!g_hash_table_remove (manager->priv->smartcards, card_name)) {
+                                g_debug ("got removal event of unknown card!");
+                        }
+                        g_free (card_name);
+                        card_name = NULL;
+                        card = NULL;
+                        break;
+
+                default:
+                        g_free (card_name);
+                        card_name = NULL;
+                        g_object_unref (card);
+
+                        should_stop = TRUE;
+                        break;
+        }
+
+out:
+        if (should_stop) {
+                GError *error;
+
+                error = g_error_new (GDM_SMARTCARD_MANAGER_ERROR,
+                                     GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS,
+                                     "%s", (condition & G_IO_IN) ? g_strerror (errno) : _("received error or hang up from event source"));
+
+                gdm_smartcard_manager_emit_error (manager, error);
+                g_error_free (error);
+                gdm_smartcard_manager_stop_now (manager);
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static void
+gdm_smartcard_manager_event_processing_stopped_handler (GdmSmartcardManager *manager)
+{
+        manager->priv->smartcard_event_source = NULL;
+        gdm_smartcard_manager_stop_now (manager);
+}
+
+static gboolean
+gdm_open_pipe (gint *write_fd,
+                  gint *read_fd)
+{
+        gint pipe_fds[2] = { -1, -1 };
+
+        g_assert (write_fd != NULL);
+        g_assert (read_fd != NULL);
+
+        if (pipe (pipe_fds) < 0) {
+                return FALSE;
+        }
+
+        if (fcntl (pipe_fds[0], F_SETFD, FD_CLOEXEC) < 0) {
+                close (pipe_fds[0]);
+                close (pipe_fds[1]);
+                return FALSE;
+        }
+
+        if (fcntl (pipe_fds[1], F_SETFD, FD_CLOEXEC) < 0) {
+                close (pipe_fds[0]);
+                close (pipe_fds[1]);
+                return FALSE;
+        }
+
+        *read_fd = pipe_fds[0];
+        *write_fd = pipe_fds[1];
+
+        return TRUE;
+}
+
+static void
+gdm_smartcard_manager_stop_watching_for_events (GdmSmartcardManager  *manager)
+{
+        if (manager->priv->smartcard_event_source != NULL) {
+                g_source_destroy (manager->priv->smartcard_event_source);
+                manager->priv->smartcard_event_source = NULL;
+        }
+
+        if (manager->priv->worker_thread != NULL) {
+                SECMOD_CancelWait (manager->priv->module);
+                /* FIXME: The function above doesn't seem to
+                 * always wake up WaitForAnyTokenEvent
+                 */
+                exit (0);
+                g_thread_join (manager->priv->worker_thread);
+                manager->priv->worker_thread = NULL;
+        }
+}
+
+static gboolean
+sc_load_nss (GError **error)
+{
+        SECStatus status = SECSuccess;
+        static const guint32 flags =
+        NSS_INIT_READONLY | NSS_INIT_NOCERTDB | NSS_INIT_NOMODDB |
+        NSS_INIT_FORCEOPEN | NSS_INIT_NOROOTINIT |
+        NSS_INIT_OPTIMIZESPACE | NSS_INIT_PK11RELOAD;
+
+        g_debug ("attempting to load NSS database '%s'",
+                  GDM_SMARTCARD_MANAGER_NSS_DB);
+
+        status = NSS_Initialize (GDM_SMARTCARD_MANAGER_NSS_DB,
+                                 "", "", SECMOD_DB, flags);
+
+        if (status != SECSuccess) {
+                gsize error_message_size;
+                char *error_message;
+
+                error_message_size = PR_GetErrorTextLength ();
+
+                if (error_message_size == 0) {
+                        g_debug ("NSS security system could not be initialized");
+                        g_set_error (error,
+                                     GDM_SMARTCARD_MANAGER_ERROR,
+                                     GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+                                     _("NSS security system could not be initialized"));
+                        goto out;
+                }
+
+                error_message = g_slice_alloc0 (error_message_size);
+                PR_GetErrorText (error_message);
+
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+                             "%s", error_message);
+                g_debug ("NSS security system could not be initialized - %s",
+                          error_message);
+
+                g_slice_free1 (error_message_size, error_message);
+
+                goto out;
+        }
+
+        g_debug ("NSS database sucessfully loaded");
+        return TRUE;
+
+out:
+        g_debug ("NSS database couldn't be sucessfully loaded");
+        return FALSE;
+}
+
+static SECMODModule *
+load_driver (char   *module_path,
+                    GError **error)
+{
+        SECMODModule *module;
+        char *module_spec;
+        gboolean module_explicitly_specified;
+
+        g_debug ("attempting to load driver...");
+
+        module = NULL;
+        module_explicitly_specified = module_path != NULL;
+        if (module_explicitly_specified) {
+                module_spec = g_strdup_printf ("library=\"%s\"", module_path);
+                g_debug ("loading smartcard driver using spec '%s'",
+                          module_spec);
+
+                module = SECMOD_LoadUserModule (module_spec,
+                                                NULL /* parent */,
+                                                FALSE /* recurse */);
+                g_free (module_spec);
+                module_spec = NULL;
+
+        } else {
+                SECMODModuleList *modules, *tmp;
+
+                modules = SECMOD_GetDefaultModuleList ();
+
+                for (tmp = modules; tmp != NULL; tmp = tmp->next) {
+                        if (!SECMOD_HasRemovableSlots (tmp->module) ||
+                            !tmp->module->loaded)
+                                continue;
+
+                        module = SECMOD_ReferenceModule (tmp->module);
+                        break;
+                }
+
+                /* fallback to compiled in driver path
+                 */
+                if (module == NULL) {
+                        module_path = GDM_SMARTCARD_MANAGER_DRIVER;
+                        module_spec = g_strdup_printf ("library=\"%s\"", module_path);
+                        g_debug ("loading smartcard driver using spec '%s'",
+                                module_spec);
+
+                        module = SECMOD_LoadUserModule (module_spec,
+                                NULL /* parent */,
+                                FALSE /* recurse */);
+                        g_free (module_spec);
+                        module_spec = NULL;
+                }
+
+        }
+
+        if (!module_explicitly_specified && module == NULL) {
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+                             _("no suitable smartcard driver could be found"));
+        } else if (module == NULL || !module->loaded) {
+
+                gsize error_message_size;
+                char *error_message;
+
+                if (module != NULL && !module->loaded) {
+                        g_debug ("module found but not loaded?!");
+                        SECMOD_DestroyModule (module);
+                        module = NULL;
+                }
+
+                error_message_size = PR_GetErrorTextLength ();
+
+                if (error_message_size == 0) {
+                        g_debug ("smartcard driver '%s' could not be loaded",
+                                  module_path);
+                        g_set_error (error,
+                                     GDM_SMARTCARD_MANAGER_ERROR,
+                                     GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+                                     _("smartcard driver '%s' could not be "
+                                       "loaded"), module_path);
+                        goto out;
+                }
+
+                error_message = g_slice_alloc0 (error_message_size);
+                PR_GetErrorText (error_message);
+
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+                             "%s", error_message);
+
+                g_debug ("smartcard driver '%s' could not be loaded - %s",
+                          module_path, error_message);
+                g_slice_free1 (error_message_size, error_message);
+        }
+
+out:
+        return module;
+}
+
+static void
+gdm_smartcard_manager_get_all_cards (GdmSmartcardManager *manager)
+{
+        int i;
+
+        for (i = 0; i < manager->priv->module->slotCount; i++) {
+                GdmSmartcard *card;
+                CK_SLOT_ID    slot_id;
+                gint          slot_series;
+                char *card_name;
+
+                slot_id = PK11_GetSlotID (manager->priv->module->slots[i]);
+                slot_series = PK11_GetSlotSeries (manager->priv->module->slots[i]);
+
+                card = _gdm_smartcard_new (manager->priv->module,
+                                                slot_id, slot_series);
+
+                card_name = gdm_smartcard_get_name (card);
+
+                g_hash_table_replace (manager->priv->smartcards,
+                                      card_name, card);
+        }
+}
+
+gboolean
+gdm_smartcard_manager_start (GdmSmartcardManager  *manager,
+                             GError              **error)
+{
+        GError *watching_error;
+        gint worker_fd;
+        GPid worker_pid;
+        GIOChannel *io_channel;
+        GSource *source;
+        GIOFlags channel_flags;
+        GError *nss_error;
+
+        if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED) {
+                g_debug ("smartcard manager already started");
+                return TRUE;
+        }
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTING;
+
+        worker_fd = -1;
+        worker_pid = 0;
+
+        nss_error = NULL;
+        if (!manager->priv->nss_is_loaded && !sc_load_nss (&nss_error)) {
+                g_propagate_error (error, nss_error);
+                goto out;
+        }
+        manager->priv->nss_is_loaded = TRUE;
+
+        if (manager->priv->module == NULL) {
+                manager->priv->module = load_driver (manager->priv->module_path, &nss_error);
+        }
+
+        if (manager->priv->module == NULL) {
+                g_propagate_error (error, nss_error);
+                goto out;
+        }
+
+        if (!gdm_smartcard_manager_create_worker (manager, &worker_fd, &manager->priv->worker_thread)) {
+
+                g_set_error (error,
+                             GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS,
+                             _("could not watch for incoming card events - %s"),
+                             g_strerror (errno));
+
+                goto out;
+        }
+
+        io_channel = g_io_channel_unix_new (worker_fd);
+
+        channel_flags = g_io_channel_get_flags (io_channel);
+        watching_error = NULL;
+
+        source = g_io_create_watch (io_channel, G_IO_IN | G_IO_HUP);
+        g_io_channel_unref (io_channel);
+        io_channel = NULL;
+
+        manager->priv->smartcard_event_source = source;
+
+        g_source_set_callback (manager->priv->smartcard_event_source,
+                               (GSourceFunc) (GIOFunc)
+                               gdm_smartcard_manager_check_for_and_process_events,
+                               manager,
+                               (GDestroyNotify)
+                               gdm_smartcard_manager_event_processing_stopped_handler);
+        g_source_attach (manager->priv->smartcard_event_source, NULL);
+        g_source_unref (manager->priv->smartcard_event_source);
+
+        /* populate the hash with cards that are already inserted
+         */
+        gdm_smartcard_manager_get_all_cards (manager);
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STARTED;
+
+out:
+        /* don't leave it in a half started state
+         */
+        if (manager->priv->state != GDM_SMARTCARD_MANAGER_STATE_STARTED) {
+                g_debug ("smartcard manager could not be completely started");
+                gdm_smartcard_manager_stop (manager);
+        } else {
+                g_debug ("smartcard manager started");
+        }
+
+        return manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STARTED;
+}
+
+static gboolean
+gdm_smartcard_manager_stop_now (GdmSmartcardManager *manager)
+{
+        if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) {
+                return FALSE;
+        }
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPED;
+        gdm_smartcard_manager_stop_watching_for_events (manager);
+
+        if (manager->priv->module != NULL) {
+                SECMOD_DestroyModule (manager->priv->module);
+                manager->priv->module = NULL;
+        }
+
+        if (manager->priv->nss_is_loaded) {
+                NSS_Shutdown ();
+                manager->priv->nss_is_loaded = FALSE;
+        }
+
+        g_debug ("smartcard manager stopped");
+
+        return FALSE;
+}
+
+static void
+gdm_smartcard_manager_queue_stop (GdmSmartcardManager *manager)
+{
+
+        manager->priv->state = GDM_SMARTCARD_MANAGER_STATE_STOPPING;
+
+        g_idle_add ((GSourceFunc) gdm_smartcard_manager_stop_now, manager);
+}
+
+void
+gdm_smartcard_manager_stop (GdmSmartcardManager *manager)
+{
+        if (manager->priv->state == GDM_SMARTCARD_MANAGER_STATE_STOPPED) {
+                return;
+        }
+
+        if (manager->priv->is_unstoppable) {
+                gdm_smartcard_manager_queue_stop (manager);
+                return;
+        }
+
+        gdm_smartcard_manager_stop_now (manager);
+}
+
+static void
+gdm_smartcard_manager_check_for_login_card (CK_SLOT_ID    slot_id,
+                                            GdmSmartcard *card,
+                                            gboolean     *is_inserted)
+{
+        g_assert (is_inserted != NULL);
+
+        if (gdm_smartcard_is_login_card (card)) {
+                *is_inserted = TRUE;
+        }
+
+}
+
+gboolean
+gdm_smartcard_manager_login_card_is_inserted (GdmSmartcardManager *manager)
+
+{
+        gboolean is_inserted;
+
+        is_inserted = FALSE;
+        g_hash_table_foreach (manager->priv->smartcards,
+                              (GHFunc)
+                              gdm_smartcard_manager_check_for_login_card,
+                              &is_inserted);
+        return is_inserted;
+}
+
+static GdmSmartcardManagerWorker *
+gdm_smartcard_manager_worker_new (gint write_fd)
+{
+        GdmSmartcardManagerWorker *worker;
+
+        worker = g_slice_new0 (GdmSmartcardManagerWorker);
+        worker->write_fd = write_fd;
+        worker->module = NULL;
+
+        worker->smartcards =
+                g_hash_table_new_full ((GHashFunc) sc_slot_id_hash,
+                                       (GEqualFunc) sc_slot_id_equal,
+                                       (GDestroyNotify) g_free,
+                                       (GDestroyNotify) g_object_unref);
+
+        return worker;
+}
+
+static void
+gdm_smartcard_manager_worker_free (GdmSmartcardManagerWorker *worker)
+{
+        if (worker->smartcards != NULL) {
+                g_hash_table_destroy (worker->smartcards);
+                worker->smartcards = NULL;
+        }
+
+        g_slice_free (GdmSmartcardManagerWorker, worker);
+}
+
+static gboolean
+sc_read_bytes (gint fd, gpointer bytes, gsize num_bytes)
+{
+        size_t bytes_left;
+        size_t total_bytes_read;
+        ssize_t bytes_read;
+
+        bytes_left = (size_t) num_bytes;
+        total_bytes_read = 0;
+
+        do {
+                bytes_read = read (fd, bytes + total_bytes_read, bytes_left);
+                g_assert (bytes_read <= (ssize_t) bytes_left);
+
+                if (bytes_read <= 0) {
+                        if ((bytes_read < 0) && (errno == EINTR || errno == EAGAIN)) {
+                                continue;
+                        }
+
+                        bytes_left = 0;
+                } else {
+                        bytes_left -= bytes_read;
+                        total_bytes_read += bytes_read;
+                }
+        } while (bytes_left > 0);
+
+        if (total_bytes_read <  (size_t) num_bytes) {
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static gboolean
+sc_write_bytes (gint fd, gconstpointer bytes, gsize num_bytes)
+{
+        size_t bytes_left;
+        size_t total_bytes_written;
+        ssize_t bytes_written;
+
+        bytes_left = (size_t) num_bytes;
+        total_bytes_written = 0;
+
+        do {
+                bytes_written = write (fd, bytes + total_bytes_written, bytes_left);
+                g_assert (bytes_written <= (ssize_t) bytes_left);
+
+                if (bytes_written <= 0) {
+                        if ((bytes_written < 0) && (errno == EINTR || errno == EAGAIN)) {
+                                continue;
+                        }
+
+                        bytes_left = 0;
+                } else {
+                        bytes_left -= bytes_written;
+                        total_bytes_written += bytes_written;
+                }
+        } while (bytes_left > 0);
+
+        if (total_bytes_written <  (size_t) num_bytes) {
+                return FALSE;
+        }
+
+        return TRUE;
+}
+
+static GdmSmartcard *
+sc_read_smartcard (gint          fd,
+                   SECMODModule *module)
+{
+        GdmSmartcard *card;
+        char *card_name;
+        gsize card_name_size;
+
+        card_name_size = 0;
+        if (!sc_read_bytes (fd, &card_name_size, sizeof (card_name_size))) {
+                return NULL;
+        }
+
+        card_name = g_slice_alloc0 (card_name_size);
+        if (!sc_read_bytes (fd, card_name, card_name_size)) {
+                g_slice_free1 (card_name_size, card_name);
+                return NULL;
+        }
+        card = _gdm_smartcard_new_from_name (module, card_name);
+        g_slice_free1 (card_name_size, card_name);
+
+        return card;
+}
+
+static gboolean
+sc_write_smartcard (gint          fd,
+                    GdmSmartcard *card)
+{
+        gsize card_name_size;
+        char *card_name;
+
+        card_name = gdm_smartcard_get_name (card);
+        card_name_size = strlen (card_name) + 1;
+
+        if (!sc_write_bytes (fd, &card_name_size, sizeof (card_name_size))) {
+                g_free (card_name);
+                return FALSE;
+        }
+
+        if (!sc_write_bytes (fd, card_name, card_name_size)) {
+                g_free (card_name);
+                return FALSE;
+        }
+        g_free (card_name);
+
+        return TRUE;
+}
+
+static gboolean
+gdm_smartcard_manager_worker_emit_smartcard_removed (GdmSmartcardManagerWorker  *worker,
+                                                     GdmSmartcard               *card,
+                                                     GError                    **error)
+{
+        g_debug ("card '%s' removed!", gdm_smartcard_get_name (card));
+
+        if (!sc_write_bytes (worker->write_fd, "R", 1)) {
+                goto error_out;
+        }
+
+        if (!sc_write_smartcard (worker->write_fd, card)) {
+                goto error_out;
+        }
+
+        return TRUE;
+
+error_out:
+        g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR,
+                     GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS,
+                     "%s", g_strerror (errno));
+        return FALSE;
+}
+
+static gboolean
+gdm_smartcard_manager_worker_emit_smartcard_inserted (GdmSmartcardManagerWorker  *worker,
+                                                      GdmSmartcard               *card,
+                                                      GError                    **error)
+{
+        GError *write_error;
+
+        write_error = NULL;
+        g_debug ("card '%s' inserted!", gdm_smartcard_get_name (card));
+        if (!sc_write_bytes (worker->write_fd, "I", 1)) {
+                goto error_out;
+        }
+
+        if (!sc_write_smartcard (worker->write_fd, card)) {
+                goto error_out;
+        }
+
+        return TRUE;
+
+error_out:
+        g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR,
+                     GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS,
+                     "%s", g_strerror (errno));
+        return FALSE;
+}
+
+static gboolean
+gdm_smartcard_manager_worker_watch_for_and_process_event (GdmSmartcardManagerWorker  *worker,
+                                                          GError                    **error)
+{
+        PK11SlotInfo *slot;
+        CK_SLOT_ID slot_id, *key;
+        gint slot_series, card_slot_series;
+        GdmSmartcard *card;
+        GError *processing_error;
+
+        g_debug ("waiting for card event");
+
+        /* FIXME: we return FALSE quite a bit in this function without cleaning up
+         * resources.  By returning FALSE we're going to ultimately exit anyway, but
+         * we should still be tidier about things.
+         */
+
+        slot = SECMOD_WaitForAnyTokenEvent (worker->module, 0, PR_SecondsToInterval (1));
+        processing_error = NULL;
+
+        if (slot == NULL) {
+                int error_code;
+
+                error_code = PORT_GetError ();
+                if ((error_code == 0) || (error_code == SEC_ERROR_NO_EVENT)) {
+                        g_debug ("spurrious event occurred");
+                        return TRUE;
+                }
+
+                /* FIXME: is there a function to convert from a PORT error
+                 * code to a translated string?
+                 */
+                g_set_error (error, GDM_SMARTCARD_MANAGER_ERROR,
+                             GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+                             _("encountered unexpected error while "
+                               "waiting for smartcard events"));
+                return FALSE;
+        }
+
+        /* the slot id and series together uniquely identify a card.
+         * You can never have two cards with the same slot id at the
+         * same time, however (I think), so we can key off of it.
+         */
+        slot_id = PK11_GetSlotID (slot);
+        slot_series = PK11_GetSlotSeries (slot);
+
+        /* First check to see if there is a card that we're currently
+         * tracking in the slot.
+         */
+        key = g_new (CK_SLOT_ID, 1);
+        *key = slot_id;
+        card = g_hash_table_lookup (worker->smartcards, key);
+
+        if (card != NULL) {
+                card_slot_series = gdm_smartcard_get_slot_series (card);
+        } else {
+                card_slot_series = -1;
+        }
+
+        if (PK11_IsPresent (slot)) {
+                /* Now, check to see if their is a new card in the slot.
+                 * If there was a different card in the slot now than
+                 * there was before, then we need to emit a removed signal
+                 * for the old card (we don't want unpaired insertion events).
+                 */
+                if ((card != NULL) &&
+                    card_slot_series != slot_series) {
+                        if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) {
+                                g_propagate_error (error, processing_error);
+                                return FALSE;
+                        }
+                }
+
+                card = _gdm_smartcard_new (worker->module,
+                                           slot_id, slot_series);
+
+                g_hash_table_replace (worker->smartcards,
+                                      key, card);
+                key = NULL;
+
+                if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) {
+                        g_propagate_error (error, processing_error);
+                        return FALSE;
+                }
+        } else {
+                /* if we aren't tracking the card, just discard the event.
+                 * We don't want unpaired remove events.  Note on startup
+                 * NSS will generate an "insertion" event if a card is
+                 * already inserted in the slot.
+                 */
+                if ((card != NULL)) {
+                        /* FIXME: i'm not sure about this code.  Maybe we
+                         * shouldn't do this at all, or maybe we should do it
+                         * n times (where n = slot_series - card_slot_series + 1)
+                         *
+                         * Right now, i'm just doing it once.
+                         */
+                        if ((slot_series - card_slot_series) > 1) {
+
+                                if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) {
+                                        g_propagate_error (error, processing_error);
+                                        return FALSE;
+                                }
+                                g_hash_table_remove (worker->smartcards, key);
+
+                                card = _gdm_smartcard_new (worker->module,
+                                                                slot_id, slot_series);
+                                g_hash_table_replace (worker->smartcards,
+                                                      key, card);
+                                key = NULL;
+                                if (!gdm_smartcard_manager_worker_emit_smartcard_inserted (worker, card, &processing_error)) {
+                                        g_propagate_error (error, processing_error);
+                                        return FALSE;
+                                }
+                        }
+
+                        if (!gdm_smartcard_manager_worker_emit_smartcard_removed (worker, card, &processing_error)) {
+                                g_propagate_error (error, processing_error);
+                                return FALSE;
+                        }
+
+                        g_hash_table_remove (worker->smartcards, key);
+                        card = NULL;
+                } else {
+                        g_debug ("got spurious remove event");
+                }
+        }
+
+        g_free (key);
+        PK11_FreeSlot (slot);
+
+        return TRUE;
+}
+
+static void
+gdm_smartcard_manager_worker_run (GdmSmartcardManagerWorker *worker)
+{
+        GError *error;
+
+
+        error = NULL;
+
+        while (gdm_smartcard_manager_worker_watch_for_and_process_event (worker, &error));
+
+        if (error != NULL)  {
+                g_debug ("could not process card event - %s", error->message);
+                g_error_free (error);
+        }
+
+        gdm_smartcard_manager_worker_free (worker);
+}
+
+static gboolean
+gdm_smartcard_manager_create_worker (GdmSmartcardManager  *manager,
+                                     gint                 *worker_fd,
+                                     GThread             **worker_thread)
+{
+        GdmSmartcardManagerWorker *worker;
+        gint write_fd, read_fd;
+
+        write_fd = -1;
+        read_fd = -1;
+        if (!gdm_open_pipe (&write_fd, &read_fd)) {
+                return FALSE;
+        }
+
+        worker = gdm_smartcard_manager_worker_new (write_fd);
+        worker->module = manager->priv->module;
+
+        *worker_thread = g_thread_create ((GThreadFunc)
+                                          gdm_smartcard_manager_worker_run,
+                                          worker, TRUE, NULL);
+
+        if (*worker_thread == NULL) {
+                gdm_smartcard_manager_worker_free (worker);
+                return FALSE;
+        }
+
+        if (worker_fd) {
+                *worker_fd = read_fd;
+        }
+
+        return TRUE;
+}
+
+#ifdef GDM_SMARTCARD_MANAGER_ENABLE_TEST
+#include <glib.h>
+
+static GMainLoop *event_loop;
+static gboolean should_exit_on_next_remove = FALSE;
+
+static gboolean
+on_timeout (GdmSmartcardManager *manager)
+{
+        GError *error;
+        g_print ("Re-enabling manager.\n");
+
+        if (!gdm_smartcard_manager_start (manager, &error)) {
+                g_warning ("could not start smartcard manager - %s",
+                           error->message);
+                g_error_free (error);
+                return 1;
+        }
+        g_print ("Please re-insert smartcard\n");
+
+        should_exit_on_next_remove = TRUE;
+
+        return FALSE;
+}
+
+static void
+on_device_inserted (GdmSmartcardManager *manager,
+                    GdmSmartcard        *card)
+{
+        g_print ("smartcard inserted!\n");
+        g_print ("Please remove it.\n");
+}
+
+static void
+on_device_removed (GdmSmartcardManager *manager,
+                   GdmSmartcard        *card)
+{
+        g_print ("smartcard removed!\n");
+
+        if (should_exit_on_next_remove) {
+                g_main_loop_quit (event_loop);
+        } else {
+                g_print ("disabling manager for 2 seconds\n");
+                gdm_smartcard_manager_stop (manager);
+                g_timeout_add (2000, (GSourceFunc) on_timeout, manager);
+        }
+}
+
+int
+main (int   argc,
+      char *argv[])
+{
+        GdmSmartcardManager *manager;
+        GError *error;
+
+        g_log_set_always_fatal (G_LOG_LEVEL_ERROR
+                                | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING);
+
+        g_type_init ();
+
+        g_message ("creating instance of 'smartcard manager' object...");
+        manager = gdm_smartcard_manager_new (NULL);
+        g_message ("'smartcard manager' object created successfully");
+
+        g_signal_connect (manager, "smartcard-inserted",
+                          G_CALLBACK (on_device_inserted), NULL);
+
+        g_signal_connect (manager, "smartcard-removed",
+                          G_CALLBACK (on_device_removed), NULL);
+
+        g_message ("starting listener...");
+
+        error = NULL;
+        if (!gdm_smartcard_manager_start (manager, &error)) {
+                g_warning ("could not start smartcard manager - %s",
+                           error->message);
+                g_error_free (error);
+                return 1;
+        }
+
+        event_loop = g_main_loop_new (NULL, FALSE);
+        g_main_loop_run (event_loop);
+        g_main_loop_unref (event_loop);
+        event_loop = NULL;
+
+        g_message ("destroying previously created 'smartcard manager' object...");
+        g_object_unref (manager);
+        manager = NULL;
+        g_message ("'smartcard manager' object destroyed successfully");
+
+        return 0;
+}
+#endif
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h	2009-03-04 21:03:53.189434924 -0500
@@ -0,0 +1,86 @@
+/* gdm-smartcard-manager.h - object for monitoring smartcard insertion and
+ *                           removal events
+ *
+ * Copyright (C) 2006, 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ * 02111-1307, USA.
+ *
+ * Written by: Ray Strode
+ */
+#ifndef GDM_SMARTCARD_MANAGER_H
+#define GDM_SMARTCARD_MANAGER_H
+
+#define GDM_SMARTCARD_ENABLE_INTERNAL_API
+#include "gdm-smartcard.h"
+
+#include <glib.h>
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+#define GDM_TYPE_SMARTCARD_MANAGER            (gdm_smartcard_manager_get_type ())
+#define GDM_SMARTCARD_MANAGER(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManager))
+#define GDM_SMARTCARD_MANAGER_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass))
+#define GDM_IS_SMARTCARD_MANAGER(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SC_TYPE_SMARTCARD_MANAGER))
+#define GDM_IS_SMARTCARD_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SC_TYPE_SMARTCARD_MANAGER))
+#define GDM_SMARTCARD_MANAGER_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS((obj), GDM_TYPE_SMARTCARD_MANAGER, GdmSmartcardManagerClass))
+#define GDM_SMARTCARD_MANAGER_ERROR           (gdm_smartcard_manager_error_quark ())
+typedef struct _GdmSmartcardManager GdmSmartcardManager;
+typedef struct _GdmSmartcardManagerClass GdmSmartcardManagerClass;
+typedef struct _GdmSmartcardManagerPrivate GdmSmartcardManagerPrivate;
+typedef enum _GdmSmartcardManagerError GdmSmartcardManagerError;
+
+struct _GdmSmartcardManager {
+    GObject parent;
+
+    /*< private > */
+    GdmSmartcardManagerPrivate *priv;
+};
+
+struct _GdmSmartcardManagerClass {
+        GObjectClass parent_class;
+
+        /* Signals */
+        void (*smartcard_inserted) (GdmSmartcardManager *manager,
+                                    GdmSmartcard        *token);
+        void (*smartcard_removed) (GdmSmartcardManager *manager,
+                                   GdmSmartcard        *token);
+        void (*error) (GdmSmartcardManager *manager,
+                       GError              *error);
+};
+
+enum _GdmSmartcardManagerError {
+    GDM_SMARTCARD_MANAGER_ERROR_GENERIC = 0,
+    GDM_SMARTCARD_MANAGER_ERROR_WITH_NSS,
+    GDM_SMARTCARD_MANAGER_ERROR_LOADING_DRIVER,
+    GDM_SMARTCARD_MANAGER_ERROR_WATCHING_FOR_EVENTS,
+    GDM_SMARTCARD_MANAGER_ERROR_REPORTING_EVENTS
+};
+
+GType gdm_smartcard_manager_get_type (void) G_GNUC_CONST;
+GQuark gdm_smartcard_manager_error_quark (void) G_GNUC_CONST;
+
+GdmSmartcardManager *gdm_smartcard_manager_new (const char *module);
+
+gboolean gdm_smartcard_manager_start (GdmSmartcardManager  *manager,
+                                      GError              **error);
+
+void gdm_smartcard_manager_stop (GdmSmartcardManager *manager);
+
+char *gdm_smartcard_manager_get_module_path (GdmSmartcardManager *manager);
+gboolean gdm_smartcard_manager_login_token_is_inserted (GdmSmartcardManager *manager);
+
+G_END_DECLS
+#endif                                /* GDM_SMARTCARD_MANAGER_H */
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard	2009-03-04 21:03:53.190448876 -0500
@@ -0,0 +1,11 @@
+#%PAM-1.0
+auth       required    pam_env.so
+auth       required    pam_pkcs11.so wait_for_card
+account    required    pam_nologin.so
+account    include     system-auth
+password   include     system-auth
+session    required    pam_loginuid.so
+session    optional    pam_console.so
+session    optional    pam_keyinit.so force revoke
+session    required    pam_namespace.so
+session    include     system-auth
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c	2009-03-04 21:03:53.191446556 -0500
@@ -0,0 +1,167 @@
+#include "config.h"
+
+#include <fcntl.h>
+#include <locale.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <glib.h>
+
+#include "gdm-smartcard-manager.h"
+#include "gdm-smartcard.h"
+
+#ifndef GDM_SMARTCARDS_CONF
+#define GDM_SMARTCARDS_CONF GDMCONFDIR "/smartcards.conf"
+#endif
+
+#ifndef GDM_SMARTCARDS_GROUP
+#define GDM_SMARTCARDS_GROUP "Smartcards"
+#endif
+
+#ifndef GDM_SMARTCARDS_KEY_ENABLED
+#define GDM_SMARTCARDS_KEY_ENABLED "Enabled"
+#endif
+
+#ifndef GDM_SMARTCARDS_KEY_DRIVER
+#define GDM_SMARTCARDS_KEY_DRIVER "Driver"
+#endif
+
+static GMainLoop *event_loop;
+static GdmSmartcardManager *manager;
+static int signal_pipe_fds[2] = { -1, -1 };
+
+static void
+on_smartcard_event (const char *event_string)
+{
+        g_debug ("smartcard event '%s' happened", event_string);
+        g_print ("%s", event_string);
+        fflush (stdout);
+}
+
+static void
+watch_for_smartcards (void)
+{
+        GError *error;
+        char *driver;
+        GKeyFile *cfg;
+
+        cfg = g_key_file_new ();
+
+        error = NULL;
+        driver = NULL;
+        if (g_key_file_load_from_file (cfg, GDM_SMARTCARDS_CONF, G_KEY_FILE_NONE, &error)) {
+                if (!g_key_file_get_boolean (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_ENABLED, &error)) {
+                        g_debug ("smartcard support is not enabled");
+                        goto out;
+                }
+
+                driver = g_key_file_get_string (cfg, GDM_SMARTCARDS_GROUP, GDM_SMARTCARDS_KEY_DRIVER, NULL);
+                g_debug ("smartcards driver is set to '%s'",
+                        driver == NULL || driver[0] == '\0'? "<automatic>" : driver);
+        }
+
+        g_debug ("watching for smartcard insertion and removal events");
+        manager = gdm_smartcard_manager_new (driver);
+        g_free (driver);
+
+        g_signal_connect_swapped (manager,
+                                  "smartcard-inserted",
+                                  G_CALLBACK (on_smartcard_event),
+                                  "I");
+
+        g_signal_connect_swapped (manager,
+                                  "smartcard-removed",
+                                  G_CALLBACK (on_smartcard_event),
+                                  "R");
+
+        error = NULL;
+        if (!gdm_smartcard_manager_start (manager, &error)) {
+            g_object_unref (manager);
+            manager = NULL;
+
+            if (error != NULL) {
+                    g_debug ("%s", error->message);
+                    g_error_free (error);
+            } else {
+                    g_debug ("could not start smartcard manager");
+
+            }
+            goto out;
+        }
+out:
+        g_key_file_free (cfg);
+}
+
+static void
+stop_watching_for_smartcards (void)
+{
+        if (manager != NULL) {
+                gdm_smartcard_manager_stop (manager);
+                g_object_unref (manager);
+                manager = NULL;
+        }
+}
+
+static void
+on_term_signal (int signal_number)
+{
+        close (signal_pipe_fds[1]);
+        signal_pipe_fds[1] = -1;
+}
+
+static gboolean
+after_term_signal (GIOChannel   *io_channel,
+                   GIOCondition  condition,
+                   gpointer      data)
+{
+        g_main_loop_quit (event_loop);
+        return FALSE;
+}
+
+static void
+on_debug_message (const char     *log_domain,
+                  GLogLevelFlags  log_level,
+                  const char     *message,
+                  gpointer        user_data)
+{
+        g_printerr ("*** DEBUG: %s\n", message);
+}
+
+int
+main (int    argc,
+      char **argv)
+{
+        GIOChannel *io_channel;
+
+        setlocale (LC_ALL, "");
+
+        g_type_init ();
+
+        g_log_set_handler (NULL, G_LOG_LEVEL_DEBUG, on_debug_message, NULL);
+
+        event_loop = g_main_loop_new (NULL, FALSE);
+
+        watch_for_smartcards ();
+
+        signal (SIGTERM, on_term_signal);
+        signal (SIGPIPE, on_term_signal);
+        if (pipe (signal_pipe_fds) != 0) {
+                return 1;
+        }
+        fcntl (signal_pipe_fds[0], F_SETFD, FD_CLOEXEC);
+        fcntl (signal_pipe_fds[1], F_SETFD, FD_CLOEXEC);
+
+        io_channel = g_io_channel_unix_new (signal_pipe_fds[0]);
+        g_io_channel_set_flags (io_channel, G_IO_FLAG_NONBLOCK, NULL);
+        g_io_channel_set_encoding (io_channel, NULL, NULL);
+        g_io_channel_set_buffered (io_channel, FALSE);
+        g_io_add_watch (io_channel, G_IO_HUP, after_term_signal, NULL);
+        g_io_channel_set_close_on_unref (io_channel, TRUE);
+        g_io_channel_unref (io_channel);
+
+        g_main_loop_run (event_loop);
+
+        stop_watching_for_smartcards ();
+
+        return 0;
+}
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/Makefile.am
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/Makefile.am	2009-03-04 21:03:53.192442070 -0500
@@ -0,0 +1,71 @@
+NULL =
+
+extensiondir = $(extensionsdatadir)/smartcard
+extension_DATA = page.ui
+
+pamservicename = gdm-smartcard
+
+AM_CPPFLAGS = \
+	-I$(top_srcdir)/common				\
+	-I$(top_srcdir)/gui/simple-greeter/libnotificationarea	\
+	-I$(top_srcdir)/gui/simple-greeter/libgdmsimplegreeter	\
+	-DDMCONFDIR=\""$(dmconfdir)"\"			\
+	-DGDMCONFDIR=\"$(gdmconfdir)\"                  \
+	-DPLUGINDATADIR=\""$(extensiondir)"\"		\
+	-DPAMSERVICENAME=\""$(pamservicename)"\"	\
+	-DSYSCONFDIR=\""$(sysconfdir)"\"		\
+	-DLIBLOCALEDIR=\""$(prefix)/lib/locale"\"	\
+	-DGNOMELOCALEDIR=\""$(datadir)/locale"\" 	\
+	-DLIBEXECDIR=\""$(libexecdir)"\" 		\
+	-DLIBDIR=\""$(libdir)"\"			\
+	-DSBINDIR=\""$(sbindir)"\"		 	\
+	$(DISABLE_DEPRECATED_CFLAGS)	\
+	$(GTK_CFLAGS)					\
+	$(SIMPLE_GREETER_CFLAGS)			\
+	$(POLKIT_GNOME_CFLAGS)				\
+	$(NULL)
+
+plugindir = $(GDM_SIMPLE_GREETER_PLUGINS_DIR)
+plugin_LTLIBRARIES = smartcard.la
+
+smartcard_la_CFLAGS =			\
+	$(SIMPLE_GREETER_CFLAGS)	\
+	$(NULL)
+
+libexec_PROGRAMS = 			\
+	gdm-smartcard-worker		\
+	$(NULL)
+
+
+smartcard_la_LDFLAGS = -module -avoid-version -export-dynamic
+smartcard_la_LIBADD = ../../../../common/libgdmcommon.la \
+			../../libgdmsimplegreeter/libgdmsimplegreeter.la
+smartcard_la_SOURCES =				\
+			gdm-smartcard-extension.h	\
+			gdm-smartcard-extension.c	\
+			plugin.c
+
+gdm_smartcard_worker_LDADD = ../../../../common/libgdmcommon.la \
+				$(DAEMON_LIBS)		\
+				$(GTHREAD_LIBS)		\
+				$(NSS_LIBS)		\
+				$(NULL)
+gdm_smartcard_worker_CFLAGS =	$(DAEMON_CFLAGS)	\
+				$(NSS_CFLAGS)		\
+				$(NULL)
+gdm_smartcard_worker_SOURCES =				\
+				gdm-smartcard.h		\
+				gdm-smartcard.c		\
+				gdm-smartcard-manager.h	\
+				gdm-smartcard-manager.c	\
+				gdm-smartcard-worker.c	\
+				$(NULL)
+
+pamdir = $(PAM_PREFIX)/pam.d
+pam_DATA = $(pamservicename)
+
+EXTRA_DIST = $(extension_DATA) $(pam_DATA)
+
+MAINTAINERCLEANFILES =                  \
+        *~                              \
+        Makefile.in
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/page.ui
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/page.ui	2009-03-04 21:03:53.193451204 -0500
@@ -0,0 +1,56 @@
+<?xml version="1.0"?>
+<interface>
+  <requires lib="gtk+" version="2.14"/>
+    <object class="GtkVBox" id="page">
+      <property name="visible">True</property>
+      <property name="orientation">vertical</property>
+      <child>
+        <object class="GtkHBox" id="auth-input-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-prompt-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="expand">False</property>
+              <property name="fill">False</property>
+              <property name="position">0</property>
+            </packing>
+          </child>
+          <child>
+            <object class="GtkEntry" id="auth-prompt-entry">
+              <property name="visible">True</property>
+              <property name="can_focus">True</property>
+              <property name="activates_default">True</property>
+            </object>
+            <packing>
+              <property name="position">1</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">0</property>
+        </packing>
+      </child>
+      <child>
+        <object class="GtkHBox" id="auth-message-box">
+          <property name="visible">True</property>
+          <child>
+            <object class="GtkLabel" id="auth-message-label">
+              <property name="visible">True</property>
+            </object>
+            <packing>
+              <property name="position">0</property>
+            </packing>
+          </child>
+        </object>
+        <packing>
+          <property name="expand">True</property>
+          <property name="fill">True</property>
+          <property name="position">1</property>
+        </packing>
+      </child>
+    </object>
+</interface>
diff -up /dev/null gdm-2.25.2/gui/simple-greeter/plugins/smartcard/plugin.c
--- /dev/null	2009-03-06 04:28:12.547006661 -0500
+++ gdm-2.25.2/gui/simple-greeter/plugins/smartcard/plugin.c	2009-03-04 21:03:53.194449722 -0500
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 Red Hat, Inc.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * Written By: Ray Strode <rstrode@redhat.com>
+ *
+ */
+
+#include "gdm-smartcard-extension.h"
+
+#include <gio/gio.h>
+#include <gtk/gtk.h>
+
+GdmGreeterExtension *
+gdm_greeter_plugin_get_extension (void)
+{
+        static GObject *extension;
+
+        if (extension != NULL) {
+                g_object_ref (extension);
+        } else {
+                extension = g_object_new (GDM_TYPE_SMARTCARD_EXTENSION, NULL);
+                g_object_add_weak_pointer (extension, (gpointer *) &extension);
+        }
+
+        return GDM_GREETER_EXTENSION (extension);
+}
commit a28cd7576027cc531e655325a9f3b4b307a1fd22
Author: Ray Strode <rstrode@redhat.com>
Date:   Mon Mar 9 15:41:12 2009 -0400

    Don't tear down greeter until pam_open_session finishes
    
    Some PAM modules ask questions at that late stage of the game,
    and so we need a greeter to forward the questions on to the
    user.

diff --git a/daemon/gdm-factory-slave.c b/daemon/gdm-factory-slave.c
index 826612e..ef6d236 100644
--- a/daemon/gdm-factory-slave.c
+++ b/daemon/gdm-factory-slave.c
@@ -278,9 +278,7 @@ on_session_accredited (GdmSession      *session,
 {
         g_debug ("GdmFactorySlave:  session user verified");
 
-        gdm_session_start_session (session, service_name);
-
-        gdm_greeter_server_reset (slave->priv->greeter_server);
+        gdm_session_open_session (session, service_name);
 }
 
 static void
@@ -298,6 +296,31 @@ on_session_accreditation_failed (GdmSession      *session,
 }
 
 static void
+on_session_opened (GdmSession      *session,
+                   const char      *service_name,
+                   GdmFactorySlave *slave)
+{
+        g_debug ("GdmFactorySlave: session opened");
+
+        gdm_session_start_session (session, service_name);
+
+        gdm_greeter_server_reset (slave->priv->greeter_server);
+}
+
+static void
+on_session_open_failed (GdmSession      *session,
+                        const char      *service_name,
+                        const char      *message,
+                        GdmFactorySlave *slave)
+{
+        g_debug ("GdmFactorySlave: could not open session: %s", message);
+
+        gdm_greeter_server_problem (slave->priv->greeter_server, service_name, _("Unable to open session"));
+
+        queue_greeter_reset (slave);
+}
+
+static void
 on_session_session_started (GdmSession      *session,
                             GdmFactorySlave *slave)
 {
@@ -767,6 +790,14 @@ gdm_factory_slave_start (GdmSlave *slave)
                           G_CALLBACK (on_session_accreditation_failed),
                           slave);
         g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session,
+                          "session-opened",
+                          G_CALLBACK (on_session_opened),
+                          slave);
+        g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session,
+                          "session-open-failed",
+                          G_CALLBACK (on_session_open_failed),
+                          slave);
+        g_signal_connect (GDM_FACTORY_SLAVE (slave)->priv->session,
                           "info",
                           G_CALLBACK (on_session_info),
                           slave);
diff --git a/daemon/gdm-product-slave.c b/daemon/gdm-product-slave.c
index dd2e1bc..93d83a1 100644
--- a/daemon/gdm-product-slave.c
+++ b/daemon/gdm-product-slave.c
@@ -635,6 +635,27 @@ on_session_accreditation_failed (GdmSession      *session,
 }
 
 static void
+on_session_opened (GdmSession      *session,
+                   const char      *service_name,
+                   GdmProductSlave *slave)
+{
+        send_dbus_string_method (slave->priv->session_relay_connection,
+                                 "SessionOpened", service_name);
+}
+
+static void
+on_session_open_failed (GdmSession      *session,
+                        const char      *service_name,
+                        const char      *message,
+                        GdmProductSlave *slave)
+{
+        send_dbus_string_string_method (slave->priv->session_relay_connection,
+                                        "SessionOpenFailed",
+                                        service_name,
+                                        message);
+}
+
+static void
 on_session_info (GdmSession      *session,
                  const char      *service_name,
                  const char      *text,
@@ -1051,7 +1072,14 @@ create_new_session (GdmProductSlave *slave)
                           "accreditation-failed",
                           G_CALLBACK (on_session_accreditation_failed),
                           slave);
-
+        g_signal_connect (slave->priv->session,
+                          "session-opened",
+                          G_CALLBACK (on_session_opened),
+                          slave);
+        g_signal_connect (slave->priv->session,
+                          "session-open-failed",
+                          G_CALLBACK (on_session_open_failed),
+                          slave);
         g_signal_connect (slave->priv->session,
                           "info",
                           G_CALLBACK (on_session_info),
diff --git a/daemon/gdm-session-direct.c b/daemon/gdm-session-direct.c
index 2fc39f3..472d91f 100644
--- a/daemon/gdm-session-direct.c
+++ b/daemon/gdm-session-direct.c
@@ -943,6 +943,58 @@ gdm_session_direct_handle_problem (GdmSessionDirect *session,
 }
 
 static DBusHandlerResult
+gdm_session_direct_handle_session_opened (GdmSessionDirect *session,
+                                          GdmSessionConversation *conversation,
+                                          DBusMessage      *message)
+{
+        DBusMessage *reply;
+        DBusError    error;
+
+        g_debug ("GdmSessionDirect: Handling SessionOpened");
+
+        dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error, DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+
+        g_debug ("GdmSessionDirect: Emitting 'session-opened' signal");
+
+        _gdm_session_session_opened (GDM_SESSION (session), conversation->service_name);
+
+        reply = dbus_message_new_method_return (message);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
+        dbus_message_unref (reply);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+gdm_session_direct_handle_open_failed (GdmSessionDirect *session,
+                                       GdmSessionConversation *conversation,
+                                       DBusMessage      *message)
+{
+        DBusMessage *reply;
+        DBusError    error;
+        const char  *text;
+
+        dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &text,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+
+        reply = dbus_message_new_method_return (message);
+        dbus_connection_send (conversation->worker_connection, reply, NULL);
+        dbus_message_unref (reply);
+
+        g_debug ("GdmSessionDirect: Emitting 'session-open-failed' signal");
+        _gdm_session_session_open_failed (GDM_SESSION (session), conversation->service_name, text);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
 gdm_session_direct_handle_session_started (GdmSessionDirect *session,
                                            GdmSessionConversation *conversation,
                                            DBusMessage      *message)
@@ -1221,6 +1273,10 @@ session_worker_message (DBusConnection *connection,
                 return gdm_session_direct_handle_accreditation_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "UsernameChanged")) {
                 return gdm_session_direct_handle_username_changed (session, conversation, message);
+        } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionOpened")) {
+                return gdm_session_direct_handle_session_opened (session, conversation, message);
+        } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "OpenFailed")) {
+                return gdm_session_direct_handle_open_failed (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "SessionStarted")) {
                 return gdm_session_direct_handle_session_started (session, conversation, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_DBUS_INTERFACE, "StartFailed")) {
@@ -2213,6 +2269,19 @@ setup_session_environment (GdmSessionDirect *session)
 }
 
 static void
+gdm_session_direct_open_session (GdmSession *session,
+                                 const char *service_name)
+{
+        GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+        GdmSessionConversation *conversation;
+
+        g_return_if_fail (session != NULL);
+
+        conversation = find_conversation_by_name (impl, service_name);
+        send_dbus_void_signal (conversation, "OpenSession");
+}
+
+static void
 gdm_session_direct_start_session (GdmSession *session,
                                   const char *service_name)
 {
@@ -2685,6 +2754,7 @@ gdm_session_iface_init (GdmSessionIface *iface)
         iface->authenticate = gdm_session_direct_authenticate;
         iface->authorize = gdm_session_direct_authorize;
         iface->accredit = gdm_session_direct_accredit;
+        iface->open_session = gdm_session_direct_open_session;
         iface->close = gdm_session_direct_close;
 
         iface->cancel = gdm_session_direct_cancel;
diff --git a/daemon/gdm-session-private.h b/daemon/gdm-session-private.h
index 860c09c..36781dd 100644
--- a/daemon/gdm-session-private.h
+++ b/daemon/gdm-session-private.h
@@ -54,6 +54,11 @@ void             _gdm_session_accredited                   (GdmSession   *sessio
 void             _gdm_session_accreditation_failed         (GdmSession   *session,
                                                             const char   *service_name,
                                                             const char   *text);
+void             _gdm_session_session_opened               (GdmSession   *session,
+                                                            const char   *service_name);
+void             _gdm_session_session_open_failed          (GdmSession   *session,
+                                                            const char   *service_name,
+                                                            const char   *message);
 void             _gdm_session_session_started              (GdmSession   *session,
                                                             const char   *service_name,
                                                             int           pid);
diff --git a/daemon/gdm-session-relay.c b/daemon/gdm-session-relay.c
index 6e15f75..3bf8ed7 100644
--- a/daemon/gdm-session-relay.c
+++ b/daemon/gdm-session-relay.c
@@ -247,6 +247,14 @@ gdm_session_relay_accredit (GdmSession *session,
 }
 
 static void
+gdm_session_relay_open_session (GdmSession *session,
+                                const char *service_name)
+{
+        GdmSessionRelay *impl = GDM_SESSION_RELAY (session);
+        send_dbus_string_signal (impl, "OpenSession", service_name);
+}
+
+static void
 gdm_session_relay_answer_query (GdmSession *session,
                                 const char *service_name,
                                 const char *text)
@@ -678,6 +686,61 @@ handle_accreditation_failed (GdmSessionRelay *session_relay,
 
         return DBUS_HANDLER_RESULT_HANDLED;
 }
+static DBusHandlerResult
+handle_session_opened (GdmSessionRelay *session_relay,
+                       DBusConnection  *connection,
+                       DBusMessage     *message)
+{
+        DBusMessage *reply;
+        DBusError    error;
+        char        *service_name;
+
+        dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
+
+        g_debug ("GdmSessionRelay: Session Opened");
+
+        reply = dbus_message_new_method_return (message);
+        dbus_connection_send (connection, reply, NULL);
+        dbus_message_unref (reply);
+
+        _gdm_session_session_opened (GDM_SESSION (session_relay), service_name);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static DBusHandlerResult
+handle_session_open_failed (GdmSessionRelay *session_relay,
+                            DBusConnection  *connection,
+                            DBusMessage     *message)
+{
+        DBusMessage *reply;
+        DBusError    error;
+        char        *service_name;
+
+        dbus_error_init (&error);
+        if (! dbus_message_get_args (message, &error,
+                                     DBUS_TYPE_STRING, &service_name,
+                                     DBUS_TYPE_INVALID)) {
+                g_warning ("ERROR: %s", error.message);
+        }
+        dbus_error_free (&error);
+
+        g_debug ("GdmSessionRelay: Session Open Failed");
+
+        reply = dbus_message_new_method_return (message);
+        dbus_connection_send (connection, reply, NULL);
+        dbus_message_unref (reply);
+
+        _gdm_session_session_open_failed (GDM_SESSION (session_relay), service_name, NULL);
+
+        return DBUS_HANDLER_RESULT_HANDLED;
+}
 
 static DBusHandlerResult
 handle_session_started (GdmSessionRelay *session_relay,
@@ -794,6 +857,10 @@ session_handle_child_message (DBusConnection *connection,
                 return handle_accredited (session_relay, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "AccreditationFailed")) {
                 return handle_accreditation_failed (session_relay, connection, message);
+        } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionOpened")) {
+                return handle_session_opened (session_relay, connection, message);
+        } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionOpenFailed")) {
+                return handle_session_open_failed (session_relay, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionStarted")) {
                 return handle_session_started (session_relay, connection, message);
         } else if (dbus_message_is_method_call (message, GDM_SESSION_RELAY_DBUS_INTERFACE, "SessionStopped")) {
@@ -1193,6 +1260,7 @@ gdm_session_iface_init (GdmSessionIface *iface)
         iface->authenticate = gdm_session_relay_authenticate;
         iface->authorize = gdm_session_relay_authorize;
         iface->accredit = gdm_session_relay_accredit;
+        iface->open_session = gdm_session_relay_open_session;
         iface->close = gdm_session_relay_close;
 
         iface->cancel = gdm_session_relay_cancel;
diff --git a/daemon/gdm-session-worker.c b/daemon/gdm-session-worker.c
index 4c37943..c6ab15c 100644
--- a/daemon/gdm-session-worker.c
+++ b/daemon/gdm-session-worker.c
@@ -2189,13 +2189,13 @@ do_open_session (GdmSessionWorker *worker)
         res = gdm_session_worker_open_user_session (worker, &error);
         if (! res) {
                 send_dbus_string_method (worker->priv->connection,
-                                         "StartFailed",
+                                         "OpenFailed",
                                          error->message);
                 g_error_free (error);
                 return;
         }
 
-        queue_state_change (worker);
+        send_dbus_void_method (worker->priv->connection, "SessionOpened");
 }
 
 static void
@@ -2311,7 +2311,7 @@ on_start_program (GdmSessionWorker *worker,
         const char *text;
         dbus_bool_t res;
 
-        /* FIXME: return error if not in ACCREDITED state */
+        /* FIXME: return error if not in SESSION_OPENED state */
 
         dbus_error_init (&error);
         res = dbus_message_get_args (message,
@@ -2450,6 +2450,14 @@ on_establish_credentials (GdmSessionWorker *worker,
 }
 
 static void
+on_open_session (GdmSessionWorker *worker,
+                 DBusMessage      *message)
+{
+        /* FIXME: return error if not in ACCREDITED state */
+        queue_state_change (worker);
+}
+
+static void
 on_reauthenticate (GdmSessionWorker *worker,
                    DBusMessage      *message)
 {
@@ -2509,6 +2517,8 @@ worker_dbus_handle_message (DBusConnection *connection,
                 on_authorize (worker, message);
         } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "EstablishCredentials")) {
                 on_establish_credentials (worker, message);
+        } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "OpenSession")) {
+                on_open_session (worker, message);
         } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "StartProgram")) {
                 on_start_program (worker, message);
         } else if (dbus_message_is_signal (message, GDM_SESSION_DBUS_INTERFACE, "Reauthenticate")) {
diff --git a/daemon/gdm-session.c b/daemon/gdm-session.c
index 9ee34af..8858071 100644
--- a/daemon/gdm-session.c
+++ b/daemon/gdm-session.c
@@ -46,6 +46,8 @@ enum {
         PROBLEM,
         INFO_QUERY,
         SECRET_INFO_QUERY,
+        SESSION_OPENED,
+        SESSION_OPEN_FAILED,
         SESSION_STARTED,
         SESSION_START_FAILED,
         SESSION_EXITED,
@@ -207,6 +209,15 @@ gdm_session_cancel (GdmSession *session)
 }
 
 void
+gdm_session_open_session (GdmSession *session,
+                          const char *service_name)
+{
+        g_return_if_fail (GDM_IS_SESSION (session));
+
+        GDM_SESSION_GET_IFACE (session)->open_session (session, service_name);
+}
+
+void
 gdm_session_start_session (GdmSession *session,
                            const char *service_name)
 {
@@ -391,6 +402,28 @@ gdm_session_class_init (gpointer g_iface)
                               G_TYPE_NONE,
                               2,
                               G_TYPE_STRING, G_TYPE_STRING);
+        signals [SESSION_OPENED] =
+                g_signal_new ("session-opened",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSessionIface, session_opened),
+                              NULL,
+                              NULL,
+                              g_cclosure_marshal_VOID__STRING,
+                              G_TYPE_NONE,
+                              1,
+                              G_TYPE_STRING);
+        signals [SESSION_OPEN_FAILED] =
+                g_signal_new ("session-open-failed",
+                              iface_type,
+                              G_SIGNAL_RUN_FIRST,
+                              G_STRUCT_OFFSET (GdmSessionIface, session_open_failed),
+                              NULL,
+                              NULL,
+                              gdm_marshal_VOID__STRING_STRING,
+                              G_TYPE_NONE,
+                              2,
+                              G_TYPE_STRING, G_TYPE_STRING);
         signals [SESSION_STARTED] =
                 g_signal_new ("session-started",
                               iface_type,
@@ -616,6 +649,23 @@ _gdm_session_problem (GdmSession   *session,
 }
 
 void
+_gdm_session_session_opened (GdmSession   *session,
+                             const char   *service_name)
+{
+        g_return_if_fail (GDM_IS_SESSION (session));
+        g_signal_emit (session, signals [SESSION_OPENED], 0, service_name);
+}
+
+void
+_gdm_session_session_open_failed (GdmSession   *session,
+                                  const char   *service_name,
+                                  const char   *text)
+{
+        g_return_if_fail (GDM_IS_SESSION (session));
+        g_signal_emit (session, signals [SESSION_OPEN_FAILED], 0, service_name, text);
+}
+
+void
 _gdm_session_session_started (GdmSession   *session,
                               const char   *service_name,
                               int           pid)
diff --git a/daemon/gdm-session.h b/daemon/gdm-session.h
index c45a770..22c2ccb 100644
--- a/daemon/gdm-session.h
+++ b/daemon/gdm-session.h
@@ -62,6 +62,8 @@ struct _GdmSessionIface
         void (* accredit)                    (GdmSession   *session,
                                               const char   *service_name,
                                               int           cred_flag);
+        void (* open_session)                (GdmSession   *session,
+                                              const char   *service_name);
         void (* answer_query)                (GdmSession   *session,
                                               const char   *service_name,
                                               const char   *text);
@@ -115,6 +117,11 @@ struct _GdmSessionIface
         void (* problem)                     (GdmSession   *session,
                                               const char   *service_name,
                                               const char   *problem);
+        void (* session_opened)              (GdmSession   *session,
+                                              const char   *service_name);
+        void (* session_open_failed)         (GdmSession   *session,
+                                              const char   *service_name,
+                                              const char   *message);
         void (* session_started)             (GdmSession   *session,
                                               const char   *service_name,
                                               int           pid);
@@ -160,6 +167,8 @@ void     gdm_session_authorize                   (GdmSession *session,
 void     gdm_session_accredit                    (GdmSession *session,
                                                   const char *service_name,
                                                   int         cred_flag);
+void     gdm_session_open_session                (GdmSession *session,
+                                                  const char *service_name);
 void     gdm_session_start_session               (GdmSession *session,
                                                   const char *service_name);
 void     gdm_session_close                       (GdmSession *session);
diff --git a/daemon/gdm-simple-slave.c b/daemon/gdm-simple-slave.c
index 1924185..d6b9724 100644
--- a/daemon/gdm-simple-slave.c
+++ b/daemon/gdm-simple-slave.c
@@ -420,7 +420,7 @@ on_session_accredited (GdmSession     *session,
                        const char     *service_name,
                        GdmSimpleSlave *slave)
 {
-        queue_start_session (slave, service_name);
+        gdm_session_open_session (session, service_name);
 }
 
 static void
@@ -454,6 +454,29 @@ on_session_accreditation_failed (GdmSession     *session,
 }
 
 static void
+on_session_opened (GdmSession     *session,
+                   const char     *service_name,
+                   GdmSimpleSlave *slave)
+{
+        queue_start_session (slave, service_name);
+}
+
+static void
+on_session_open_failed (GdmSession     *session,
+                        const char     *service_name,
+                        const char     *message,
+                        GdmSimpleSlave *slave)
+{
+        if (slave->priv->greeter_server != NULL) {
+                gdm_greeter_server_problem (slave->priv->greeter_server,
+                                            service_name,
+                                            _("Unable to open session"));
+        }
+
+        gdm_session_stop_conversation (session, service_name);
+}
+
+static void
 on_session_info (GdmSession     *session,
                  const char     *service_name,
                  const char     *text,
@@ -685,6 +708,14 @@ create_new_session (GdmSimpleSlave *slave)
                           G_CALLBACK (on_session_accreditation_failed),
                           slave);
         g_signal_connect (slave->priv->session,
+                          "session-opened",
+                          G_CALLBACK (on_session_opened),
+                          slave);
+        g_signal_connect (slave->priv->session,
+                          "session-open-failed",
+                          G_CALLBACK (on_session_open_failed),
+                          slave);
+        g_signal_connect (slave->priv->session,
                           "info",
                           G_CALLBACK (on_session_info),
                           slave);