diff --git a/gdm-multistack.patch b/gdm-multistack.patch
new file mode 100644
index 0000000..4f97fef
--- /dev/null
+++ b/gdm-multistack.patch
@@ -0,0 +1,14199 @@
+diff -up gdm-2.28.0/common/gdm-marshal.list.multistack gdm-2.28.0/common/gdm-marshal.list
+--- gdm-2.28.0/common/gdm-marshal.list.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/common/gdm-marshal.list 2009-09-23 18:46:06.861400723 -0400
+@@ -5,3 +5,4 @@ VOID:STRING,STRING
+ VOID:UINT,UINT
+ VOID:STRING,INT
+ VOID:DOUBLE
++BOOLEAN:STRING
+diff -up gdm-2.28.0/configure.ac.multistack gdm-2.28.0/configure.ac
+--- gdm-2.28.0/configure.ac.multistack 2009-09-23 18:46:06.497641172 -0400
++++ gdm-2.28.0/configure.ac 2009-09-23 18:46:06.800400573 -0400
+@@ -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 @@ LIBXKLAVIER_REQUIRED_VERSION=4.0
+ LIBCANBERRA_GTK_REQUIRED_VERSION=0.4
+ #FONTCONFIG_REQUIRED_VERSION=2.6.0
+ FONTCONFIG_REQUIRED_VERSION=2.5.0
++NSS_REQUIRED_VERSION=3.11.1
+
+ EXTRA_COMPILE_WARNINGS(yes)
+
+@@ -75,6 +92,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
+@@ -183,6 +206,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 ---------------------------------------------------------------------------
+
+@@ -1242,6 +1274,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=
],
++ [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 ---------------------------------------------------------------------------
+
+@@ -1368,6 +1416,18 @@ 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/fingerprint/icons/Makefile
++gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile
++gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile
++gui/simple-greeter/plugins/smartcard/Makefile
++gui/simple-greeter/plugins/smartcard/icons/Makefile
++gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile
++gui/simple-greeter/plugins/smartcard/icons/48x48/Makefile
+ gui/simple-chooser/Makefile
+ gui/user-switch-applet/Makefile
+ utils/Makefile
+diff -up gdm-2.28.0/daemon/gdm-factory-slave.c.multistack gdm-2.28.0/daemon/gdm-factory-slave.c
+--- gdm-2.28.0/daemon/gdm-factory-slave.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-factory-slave.c 2009-09-23 18:46:06.673650996 -0400
+@@ -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,65 @@ 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_greeter_server_reset (slave->priv->greeter_server);
++ gdm_session_open_session (session, service_name);
+ }
+
+ 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);
++}
++
++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);
+ }
+@@ -366,37 +403,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 +541,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 +746,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",
+@@ -738,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 -up gdm-2.28.0/daemon/gdm-greeter-server.c.multistack gdm-2.28.0/daemon/gdm-greeter-server.c
+--- gdm-2.28.0/daemon/gdm-greeter-server.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-greeter-server.c 2009-09-23 18:46:06.658640357 -0400
+@@ -43,6 +43,7 @@
+ #include
+ #include
+
++#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,
+ " \n"
++ " \n"
++ " \n"
++ " \n"
++ " \n"
++ " \n"
++ " \n"
+ " \n"
++ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
+@@ -728,18 +841,23 @@ do_introspect (DBusConnection *connectio
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
+@@ -752,7 +870,6 @@ do_introspect (DBusConnection *connectio
+ " \n"
+ " \n"
+ " \n"
+- " \n"
+ " \n"
+ " \n"
+ " \n"
+@@ -760,10 +877,15 @@ do_introspect (DBusConnection *connectio
+ " \n"
+ " \n"
+ " \n"
++ " \n"
++ " \n"
++ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
+ " \n"
++ " \n"
+ " \n"
+ " \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.28.0/daemon/gdm-greeter-server.h.multistack gdm-2.28.0/daemon/gdm-greeter-server.h
+--- gdm-2.28.0/daemon/gdm-greeter-server.h.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-greeter-server.h 2009-09-23 18:46:06.659640973 -0400
+@@ -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.28.0/daemon/gdm-product-slave.c.multistack gdm-2.28.0/daemon/gdm-product-slave.c
+--- gdm-2.28.0/daemon/gdm-product-slave.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-product-slave.c 2009-09-23 18:46:06.676640203 -0400
+@@ -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,133 @@ 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_string_method (slave->priv->session_relay_connection,
++ "AccreditationFailed",
++ service_name,
++ message);
++}
++
++static void
++on_session_opened (GdmSession *session,
++ const char *service_name,
++ GdmProductSlave *slave)
++{
+ send_dbus_string_method (slave->priv->session_relay_connection,
+- "AccreditationFailed",
+- message);
++ "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,
+ 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 +760,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 +854,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 +946,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 +1029,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",
+@@ -875,7 +1072,14 @@ create_new_session (GdmProductSlave *sla
+ "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),
+@@ -991,8 +1195,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.28.0/daemon/gdm-session.c.multistack gdm-2.28.0/daemon/gdm-session.c
+--- gdm-2.28.0/daemon/gdm-session.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-session.c 2009-09-23 18:46:06.690641636 -0400
+@@ -24,11 +24,13 @@
+ #include
+ #include
+
++#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,
+@@ -44,6 +46,8 @@ enum {
+ PROBLEM,
+ INFO_QUERY,
+ SECRET_INFO_QUERY,
++ SESSION_OPENED,
++ SESSION_OPEN_FAILED,
+ SESSION_STARTED,
+ SESSION_START_FAILED,
+ SESSION_EXITED,
+@@ -78,11 +82,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 +127,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 +209,21 @@ gdm_session_cancel (GdmSession *session)
+ }
+
+ void
+-gdm_session_start_session (GdmSession *session)
++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)
+ {
+ 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 +231,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 +258,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 +269,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 +301,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 +311,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 +322,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 +332,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 +343,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 +353,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 +365,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 +376,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 +387,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 +398,32 @@ gdm_session_class_init (gpointer g_iface
+ G_STRUCT_OFFSET (GdmSessionIface, problem),
+ NULL,
+ NULL,
++ gdm_marshal_VOID__STRING_STRING,
++ 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,
+@@ -370,10 +431,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 +442,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 +525,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 +559,128 @@ _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_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)
+ {
+ 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 +700,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.28.0/daemon/gdm-session-direct.c.multistack gdm-2.28.0/daemon/gdm-session-direct.c
+--- gdm-2.28.0/daemon/gdm-session-direct.c.multistack 2009-09-23 18:46:06.502641319 -0400
++++ gdm-2.28.0/daemon/gdm-session-direct.c 2009-09-23 19:35:10.029394668 -0400
+@@ -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,12 @@ 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 +127,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,
+- const char *name,
++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 +168,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 ? name : "(null)");
+ }
+@@ -168,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
+@@ -226,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,
+@@ -235,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;
+@@ -243,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;
+@@ -268,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), text);
++ _gdm_session_setup_failed (GDM_SESSION (session), conversation->service_name, text);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+@@ -281,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;
+@@ -289,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));
+@@ -299,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;
+@@ -314,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");
+@@ -326,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;
+@@ -334,18 +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);
+
+- session->priv->is_authenticated = TRUE;
+- _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;
+@@ -360,20 +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");
+
+- session->priv->is_authenticated = FALSE;
+- _gdm_session_authentication_failed (GDM_SESSION (session), text);
++ _gdm_session_authentication_failed (GDM_SESSION (session), conversation->service_name, text);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ static DBusHandlerResult
+ gdm_session_direct_handle_authorized (GdmSessionDirect *session,
+- DBusConnection *connection,
++ GdmSessionConversation *conversation,
+ DBusMessage *message)
+ {
+ DBusMessage *reply;
+@@ -381,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;
+@@ -406,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), text);
++ _gdm_session_authorization_failed (GDM_SESSION (session), conversation->service_name, text);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+
+ static DBusHandlerResult
+ gdm_session_direct_handle_accredited (GdmSessionDirect *session,
+- DBusConnection *connection,
++ GdmSessionConversation *conversation,
+ DBusMessage *message)
+ {
+ DBusMessage *reply;
+@@ -426,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;
+@@ -451,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), text);
++ _gdm_session_accreditation_failed (GDM_SESSION (session), conversation->service_name, text);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+ }
+@@ -756,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;
+@@ -771,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'",
+@@ -788,59 +829,62 @@ 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);
++ conversation = find_conversation_by_name (session, service_name);
++ 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;
+@@ -853,17 +897,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;
+@@ -876,17 +920,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;
+@@ -901,27 +945,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;
+@@ -929,7 +973,7 @@ gdm_session_direct_handle_cancel_pending
+
+ static DBusHandlerResult
+ gdm_session_direct_handle_problem (GdmSessionDirect *session,
+- DBusConnection *connection,
++ GdmSessionConversation *conversation,
+ DBusMessage *message)
+ {
+ DBusMessage *reply;
+@@ -944,18 +988,70 @@ 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_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,
+- DBusConnection *connection,
++ GdmSessionConversation *conversation,
+ DBusMessage *message)
+ {
+ DBusMessage *reply;
+@@ -974,7 +1070,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'",
+@@ -983,14 +1079,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;
+@@ -1005,18 +1101,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;
+@@ -1031,7 +1127,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'",
+@@ -1045,7 +1141,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;
+@@ -1060,7 +1156,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'",
+@@ -1074,7 +1170,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;
+@@ -1089,7 +1185,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,
+@@ -1107,7 +1203,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;
+@@ -1122,7 +1218,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,
+@@ -1140,7 +1236,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;
+@@ -1155,7 +1251,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)) {
+@@ -1183,54 +1279,61 @@ 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, "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, "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, 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;
+@@ -1464,6 +1567,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,
+@@ -1478,41 +1602,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
+@@ -1558,6 +1749,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,
+@@ -1581,8 +1783,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,
+@@ -1593,15 +1798,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");
+ }
+@@ -1609,98 +1807,149 @@ 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) {
+- _gdm_session_authentication_failed (GDM_SESSION (session), NULL);
+- } else if (session->priv->is_running) {
+- _gdm_session_session_exited (GDM_SESSION (session), code);
++ g_object_ref (conversation);
++ 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) {
+- _gdm_session_authentication_failed (GDM_SESSION (session), NULL);
+- } else if (session->priv->is_running) {
+- _gdm_session_session_died (GDM_SESSION (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_object_ref (conversation);
++ if (conversation->session->priv->is_running) {
++ _gdm_session_session_died (GDM_SESSION (conversation->session), signum);
++ }
++
++ 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);
++
++ 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);
+
+- res = gdm_session_worker_job_start (session->priv->job);
++ conversation->worker_pid = gdm_session_worker_job_get_pid (conversation->job);
+
+- return res;
++ 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_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: starting conversation");
++
++ conversation = start_conversation (impl, service_name);
++
++ g_hash_table_insert (impl->priv->conversations,
++ g_strdup (service_name), conversation);
+ }
+
+ static void
+-gdm_session_direct_open (GdmSession *session)
++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: Opening session");
++ g_debug ("GdmSessionDirect: stopping conversation");
++
++ conversation = find_conversation_by_name (impl, service_name);
+
+- start_worker (impl);
++ if (conversation != NULL) {
++ stop_conversation (conversation);
++ g_hash_table_remove (impl->priv->conversations, service_name);
++ }
+ }
+
+ static void
+@@ -1713,6 +1962,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);
+
+@@ -1750,7 +2000,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");
+ }
+
+@@ -1768,6 +2019,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);
+
+@@ -1811,7 +2063,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");
+ }
+
+@@ -1825,7 +2078,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);
+@@ -1839,7 +2091,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);
+@@ -1849,42 +2100,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 ();
+@@ -1892,9 +2157,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;
+@@ -1907,7 +2172,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");
+ }
+
+@@ -1915,12 +2180,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 *
+@@ -2037,27 +2303,99 @@ setup_session_environment (GdmSessionDir
+ }
+
+ static void
+-gdm_session_direct_start_session (GdmSession *session)
++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
++stop_all_other_conversations (GdmSessionDirect *session,
++ GdmSessionConversation *conversation_to_keep)
++{
++ GHashTableIter iter;
++ gpointer key, value;
++
++ if (session->priv->conversations == NULL) {
++ return;
++ }
++
++ if (conversation_to_keep == NULL) {
++ g_debug ("GdmSessionDirect: Stopping all conversations");
++ } else {
++ g_debug ("GdmSessionDirect: Stopping all conversations "
++ "except for %s", conversation_to_keep->service_name);
++ }
++
++ 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 == conversation_to_keep) {
++ g_hash_table_iter_steal (&iter);
++ g_free (key);
++ } else {
++ stop_conversation (conversation);
++ }
++ }
++
++ g_hash_table_remove_all (session->priv->conversations);
++
++ if (conversation_to_keep != NULL) {
++ g_hash_table_insert (session->priv->conversations,
++ g_strdup (conversation_to_keep->service_name),
++ conversation_to_keep);
++ }
++}
++
++static void
++gdm_session_direct_start_session (GdmSession *session,
++ const char *service_name)
++{
++ GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
++ GdmSessionConversation *conversation;
+ char *command;
+ char *program;
+
+ g_return_if_fail (session != NULL);
+ g_return_if_fail (impl->priv->is_running == FALSE);
+
++ conversation = find_conversation_by_name (impl, service_name);
++
++ if (conversation == NULL) {
++ g_warning ("GdmSessionDirect: Tried to start session of "
++ "nonexistent conversation %s", service_name);
++ return;
++ }
++
++ stop_all_other_conversations (impl, conversation);
++
+ command = get_session_command (impl);
+ program = g_strdup_printf (GDMCONFDIR "/Xsession \"%s\"", command);
+ g_free (command);
+
+ 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)
++{
++ stop_all_other_conversations (session, NULL);
++}
++
++static void
+ gdm_session_direct_close (GdmSession *session)
+ {
+ GdmSessionDirect *impl = GDM_SESSION_DIRECT (session);
+@@ -2066,18 +2404,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;
+
+@@ -2104,30 +2445,27 @@ 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 *
+@@ -2143,6 +2481,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);
+
+@@ -2152,8 +2492,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
+@@ -2161,6 +2508,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);
+
+@@ -2170,8 +2519,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
+@@ -2179,6 +2535,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);
+
+@@ -2188,8 +2546,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_layout_name (impl));
++ }
+ }
+
+ static void
+@@ -2448,12 +2813,14 @@ 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;
+ 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 -up gdm-2.28.0/daemon/gdm-session.h.multistack gdm-2.28.0/daemon/gdm-session.h
+--- gdm-2.28.0/daemon/gdm-session.h.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-session.h 2009-09-23 18:46:06.691641204 -0400
+@@ -45,18 +45,27 @@ 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 (* open_session) (GdmSession *session,
++ const char *service_name);
+ void (* answer_query) (GdmSession *session,
++ const char *service_name,
+ const char *text);
+ void (* select_language) (GdmSession *session,
+ const char *text);
+@@ -66,44 +75,67 @@ 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_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);
+ 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 +150,31 @@ 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_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);
+
+ 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.28.0/daemon/gdm-session-private.h.multistack gdm-2.28.0/daemon/gdm-session-private.h
+--- gdm-2.28.0/daemon/gdm-session-private.h.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-session-private.h 2009-09-23 18:46:06.681641189 -0400
+@@ -27,25 +27,43 @@
+ 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_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);
+ 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 +84,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.28.0/daemon/gdm-session-relay.c.multistack gdm-2.28.0/daemon/gdm-session-relay.c
+--- gdm-2.28.0/daemon/gdm-session-relay.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-session-relay.c 2009-09-23 18:46:06.684641082 -0400
+@@ -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 ();
+@@ -243,11 +247,20 @@ gdm_session_relay_accredit (GdmSession *
+ }
+
+ 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)
+ {
+ 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 +304,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 +347,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 +364,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 +376,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 +395,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 +407,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 +426,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 +438,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 +457,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 +469,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 +485,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 +497,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 +513,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 +526,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 +542,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 +554,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 +570,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 +582,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 +598,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 +610,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 +626,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 +638,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 +654,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 +666,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 +682,62 @@ 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;
++}
++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;
+ }
+@@ -616,6 +749,7 @@ handle_session_started (GdmSessionRelay
+ {
+ DBusMessage *reply;
+ DBusError error;
++ char *service_name;
+ int pid;
+
+ dbus_error_init (&error);
+@@ -623,6 +757,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 +770,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 +800,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;
+ }
+@@ -715,12 +857,16 @@ session_handle_child_message (DBusConnec
+ 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")) {
+ 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 +895,8 @@ do_introspect (DBusConnection *connectio
+ /* interface */
+ xml = g_string_append (xml,
+ " \n"
+- " \n"
++ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
+@@ -810,7 +957,8 @@ do_introspect (DBusConnection *connectio
+ " \n"
+ " \n"
+
+- " \n"
++ " \n"
++ " \n"
+ " \n"
+ " \n"
+ " \n"
+@@ -1106,12 +1254,13 @@ 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;
+ 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 -up gdm-2.28.0/daemon/gdm-session-worker.c.multistack gdm-2.28.0/daemon/gdm-session-worker.c
+--- gdm-2.28.0/daemon/gdm-session-worker.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-session-worker.c 2009-09-23 18:46:06.895410636 -0400
+@@ -2302,8 +2302,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),
+@@ -2439,13 +2437,13 @@ do_open_session (GdmSessionWorker *worke
+ 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
+@@ -2561,7 +2559,7 @@ on_start_program (GdmSessionWorker *work
+ const char *text;
+ dbus_bool_t res;
+
+- if (worker->priv->state != GDM_SESSION_WORKER_STATE_ACCREDITED) {
++ if (worker->priv->state != GDM_SESSION_WORKER_STATE_SESSION_OPENED) {
+ g_debug ("GdmSessionWorker: ignoring spurious start program while in state %s", get_state_name (worker->priv->state));
+ return;
+ }
+@@ -2720,6 +2718,14 @@ on_establish_credentials (GdmSessionWork
+ }
+
+ 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)
+ {
+@@ -2791,6 +2797,8 @@ worker_dbus_handle_message (DBusConnecti
+ 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")) {
+@@ -2848,6 +2856,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,
+@@ -2874,6 +2904,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);
+
+@@ -2915,6 +2950,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.28.0/daemon/gdm-session-worker-job.c.multistack gdm-2.28.0/daemon/gdm-session-worker-job.c
+--- gdm-2.28.0/daemon/gdm-session-worker-job.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-session-worker-job.c 2009-09-23 18:46:06.896400986 -0400
+@@ -68,7 +68,6 @@ enum {
+
+ enum {
+ STARTED,
+- STOPPED,
+ EXITED,
+ DIED,
+ LAST_SIGNAL
+@@ -150,6 +149,37 @@ copy_environment_to_hash (GHashTable *ha
+ }
+
+ 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;
+@@ -172,31 +202,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,
+@@ -205,6 +235,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);
+
+@@ -221,7 +254,6 @@ gdm_session_worker_job_spawn (GdmSession
+ (GChildWatchFunc)session_worker_job_child_watch,
+ session_worker_job);
+
+- g_strfreev (argv);
+ out:
+
+ return ret;
+@@ -234,13 +266,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) {
+
+@@ -287,7 +320,8 @@ 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);
++ res = gdm_signal_pid (session_worker_job->priv->pid, SIGKILL);
++
+ if (res < 0) {
+ g_warning ("Unable to kill session worker process");
+ } else {
+@@ -297,6 +331,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)
+@@ -390,16 +431,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.28.0/daemon/gdm-session-worker-job.h.multistack gdm-2.28.0/daemon/gdm-session-worker-job.h
+--- gdm-2.28.0/daemon/gdm-session-worker-job.h.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/gdm-session-worker-job.h 2009-09-23 18:46:06.592642295 -0400
+@@ -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.28.0/daemon/gdm-simple-slave.c.multistack gdm-2.28.0/daemon/gdm-simple-slave.c
+--- gdm-2.28.0/daemon/gdm-simple-slave.c.multistack 2009-09-23 18:46:06.484641543 -0400
++++ gdm-2.28.0/daemon/gdm-simple-slave.c 2009-09-23 18:46:06.877395356 -0400
+@@ -71,6 +71,8 @@ struct GdmSimpleSlavePrivate
+ guint greeter_reset_id;
+ guint start_session_id;
+
++ char *start_session_service_name;
++
+ int ping_interval;
+
+ GPid server_pid;
+@@ -102,6 +104,7 @@ static void start_greeter (GdmSimpl
+
+ static void
+ on_session_started (GdmSession *session,
++ const char *service_name,
+ int pid,
+ GdmSimpleSlave *slave)
+ {
+@@ -175,7 +178,6 @@ reset_session (GdmSimpleSlave *slave)
+ {
+ destroy_session (slave);
+ create_new_session (slave);
+- gdm_session_open (GDM_SESSION (slave->priv->session));
+ }
+
+ static gboolean
+@@ -206,23 +208,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,
+- message != NULL ? message: _("Unable to initialize login system"));
++ service_name,
++ message != NULL ? message: _("Unable to initialize login system"));
+ }
+
+- destroy_session (slave);
+- queue_greeter_reset (slave);
++ gdm_session_stop_conversation (session, service_name);
+ }
+
+ static void
+@@ -242,26 +246,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,
+ message != NULL ? message : _("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;
+@@ -282,7 +290,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;
+ }
+@@ -290,29 +298,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,
+- message != NULL ? message : _("Unable to authorize user"));
++ service_name,
++ message != NULL ? message : _("Unable to authorize user"));
+ }
+
+- destroy_session (slave);
+- queue_greeter_reset (slave);
++ gdm_session_stop_conversation (session, service_name);
+ }
+
+ static gboolean
+@@ -388,31 +398,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);
++ gdm_session_open_session (session, service_name);
+ }
+
+ static void
+ on_session_accreditation_failed (GdmSession *session,
++ const char *service_name,
+ const char *message,
+ GdmSimpleSlave *slave)
+ {
+@@ -433,6 +450,7 @@ on_session_accreditation_failed (GdmSess
+ problem = _("Unable to establish credentials");
+ }
+ gdm_greeter_server_problem (slave->priv->greeter_server,
++ service_name,
+ problem);
+ }
+ }
+@@ -441,62 +459,90 @@ 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_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,
+ 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");
+ }
+@@ -512,8 +558,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);
+ }
+
+@@ -521,6 +569,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 != 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)
+@@ -605,8 +670,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",
+@@ -649,6 +718,14 @@ create_new_session (GdmSimpleSlave *slav
+ 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);
+@@ -737,12 +814,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
+@@ -751,6 +845,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);
+@@ -758,21 +855,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
+@@ -780,6 +885,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);
+ }
+
+@@ -788,6 +896,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);
+ }
+
+@@ -796,6 +907,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);
+ }
+
+@@ -812,7 +926,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
+@@ -822,8 +940,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,
+@@ -837,21 +956,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;
+ }
+
+@@ -922,6 +1049,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);
+@@ -1031,8 +1162,18 @@ idle_connect_to_display (GdmSimpleSlave
+ if (! enabled || delay > 0) {
+ start_greeter (slave);
+ create_new_session (slave);
++
++ if (enabled) {
++ g_debug ("GdmSimpleSlave: Starting timed login conversation");
++ gdm_session_start_conversation (GDM_SESSION (slave->priv->session),
++ "gdm-autologin");
++ }
+ } else {
+ reset_session (slave);
++
++ g_debug ("GdmSimpleSlave: Starting automatic login conversation");
++ gdm_session_start_conversation (GDM_SESSION (slave->priv->session),
++ "gdm-autologin");
+ }
+ } else {
+ if (slave->priv->connection_attempts >= MAX_CONNECT_ATTEMPTS) {
+diff -up gdm-2.28.0/daemon/test-session.c.multistack gdm-2.28.0/daemon/test-session.c
+--- gdm-2.28.0/daemon/test-session.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/daemon/test-session.c 2009-09-23 18:46:06.649640679 -0400
+@@ -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.28.0/gui/simple-greeter/gdm-chooser-widget.c.multistack gdm-2.28.0/gui/simple-greeter/gdm-chooser-widget.c
+--- gdm-2.28.0/gui/simple-greeter/gdm-chooser-widget.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-chooser-widget.c 2009-09-23 18:46:06.773650311 -0400
+@@ -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;
+@@ -2171,13 +2172,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;
+ }
+@@ -2524,8 +2542,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.28.0/gui/simple-greeter/gdm-chooser-widget.h.multistack gdm-2.28.0/gui/simple-greeter/gdm-chooser-widget.h
+--- gdm-2.28.0/gui/simple-greeter/gdm-chooser-widget.h.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-chooser-widget.h 2009-09-23 18:46:06.775655105 -0400
+@@ -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.28.0/gui/simple-greeter/gdm-greeter-client.c.multistack gdm-2.28.0/gui/simple-greeter/gdm-greeter-client.c
+--- gdm-2.28.0/gui/simple-greeter/gdm-greeter-client.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-client.c 2009-09-23 18:46:06.721641040 -0400
+@@ -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.28.0/gui/simple-greeter/gdm-greeter-client.h.multistack gdm-2.28.0/gui/simple-greeter/gdm-greeter-client.h
+--- gdm-2.28.0/gui/simple-greeter/gdm-greeter-client.h.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-client.h 2009-09-23 18:46:06.722645987 -0400
+@@ -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.28.0/gui/simple-greeter/gdm-greeter-login-window.c.multistack gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.c
+--- gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.c 2009-09-23 18:46:06.871400249 -0400
+@@ -1,7 +1,7 @@
+ /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*-
+ *
+ * Copyright (C) 2007 William Jon McCann
+- * 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
++ * Ray Strode
++ *
+ */
+
+ #include "config.h"
+@@ -56,12 +59,16 @@
+ #include
+ #include
+
++#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
+@@ -109,7 +116,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;
+ guint user_chooser_loaded : 1;
+@@ -138,6 +147,7 @@ enum {
+ };
+
+ enum {
++ START_CONVERSATION,
+ BEGIN_AUTO_LOGIN,
+ BEGIN_VERIFICATION,
+ BEGIN_VERIFICATION_FOR_USER,
+@@ -162,6 +172,8 @@ static void on_user_unchosen
+ static void switch_mode (GdmGreeterLoginWindow *login_window,
+ int number);
+ static void update_banner_message (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)
+
+@@ -187,9 +199,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);
+
+@@ -199,27 +208,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
+@@ -385,30 +410,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
+ on_login_button_clicked_start_other (GtkButton *button,
+@@ -427,6 +498,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);
+@@ -439,6 +511,12 @@ 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_START_OTHER:
+@@ -446,11 +524,18 @@ set_log_in_button_mode (GdmGreeterLoginW
+ gtk_widget_show (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:
+@@ -564,6 +649,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);
+@@ -592,7 +678,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);
+@@ -603,11 +690,19 @@ switch_mode (GdmGreeterLoginWindow *logi
+ default_name = NULL;
+ break;
+ case MODE_AUTHENTICATION:
++ gtk_widget_set_size_request (GTK_WIDGET (login_window),
++ GTK_WIDGET (login_window)->allocation.width,
++ -1);
+ show_widget (login_window, "cancel-button", TRUE);
+ show_widget (login_window, "shutdown-button", FALSE);
+ 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:
+@@ -687,25 +782,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);
+@@ -729,18 +853,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));
+@@ -752,21 +873,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));
+@@ -776,37 +937,122 @@ 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);
++
++ /* If the password conversation failed, then start over
++ *
++ * FIXME: we need to get this policy out of the source code
++ */
++ if (strcmp (service_name, "gdm-password") == 0) {
++ g_debug ("GdmGreeterLoginWindow: main conversation failed, starting over");
++ restart_conversations (login_window);
++ return TRUE;
++ }
++
++ 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;
+@@ -844,11 +1090,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 "
+@@ -858,8 +1114,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
+@@ -882,24 +1138,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);
+
+- show_widget (login_window, "auth-input-box", TRUE);
++ if (task != NULL) {
++ gdm_conversation_ask_question (GDM_CONVERSATION (task),
++ text);
++ g_object_unref (task);
++ }
++
++ 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));
+@@ -911,22 +1168,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));
+@@ -937,13 +1195,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
+@@ -1153,43 +1414,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 (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);
++
++ g_signal_emit (G_OBJECT (login_window), signals[USER_SELECTED],
++ 0, 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
+@@ -1328,6 +1729,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,
+@@ -1350,6 +1814,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);
+@@ -1360,7 +1826,6 @@ custom_widget_constructor (GladeXML
+ static void
+ load_theme (GdmGreeterLoginWindow *login_window)
+ {
+- GtkWidget *entry;
+ GtkWidget *button;
+ GtkWidget *box;
+ GtkWidget *image;
+@@ -1414,7 +1879,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",
+@@ -1428,8 +1893,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);
+@@ -1445,14 +1922,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");
+@@ -1609,6 +2078,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),
+@@ -1625,9 +2103,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),
+@@ -1635,9 +2113,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),
+@@ -1645,9 +2123,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),
+@@ -1685,9 +2163,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,
+@@ -1740,6 +2218,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.28.0/gui/simple-greeter/gdm-greeter-login-window.glade.multistack gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.glade
+--- gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.glade.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.glade 2009-09-23 18:46:06.730661253 -0400
+@@ -611,30 +611,29 @@
+
+
+
+-
+- True
+-
+- False
+- False
+- GTK_JUSTIFY_CENTER
+- True
+- False
+- 0.5
+- 0.5
+- 0
+- 0
+- PANGO_ELLIPSIZE_NONE
+- -1
+- False
+- 0
+-
+-
+- 0
+- True
+- True
+-
++
++ True
++
++ False
++ False
++ GTK_JUSTIFY_CENTER
++ True
++ False
++ 0.5
++ 0.5
++ 0
++ 0
++ PANGO_ELLIPSIZE_NONE
++ -1
++ False
++ 0
++
++
++ 0
++ False
++ False
++
+
+-
+
+
+ True
+@@ -654,42 +653,17 @@
+ 10
+
+
+-
+- True
+- 0
+- 0
+- Tue, 18 Nov 2008 21:55:38 GMT
+-
+-
+- 0
+- True
+- True
+-
+-
+-
+-
+-
++
+ True
+ False
+- 6
++ 0
+
+
+-
++
+ True
+-
+- False
+- False
+- GTK_JUSTIFY_LEFT
+- False
+- False
+- 0.5
+- 0.5
+- 0
+- 0
+- PANGO_ELLIPSIZE_NONE
+- -1
+- False
+- 0
++ 0
++ 0
++ Fri, 30 Jan 2009 16:03:30 GMT
+
+
+ 0
+@@ -699,16 +673,10 @@
+
+
+
+-
+- True
+- True
+- GDK_POINTER_MOTION_MASK | GDK_POINTER_MOTION_HINT_MASK | GDK_BUTTON_PRESS_MASK | GDK_BUTTON_RELEASE_MASK
+- True
+- True
+- 0
+-
+- True
+- True
++
++ 0
++ 0
++ Tue, 18 Nov 2008 21:55:38 GMT
+
+
+ 0
+@@ -717,46 +685,21 @@
+
+
+
+-
+-
+-
+
+
+ 0
+- False
+- False
++ True
++ True
+
+
+
+
+-
++
+ True
+ False
+ 0
+-
+
+-
+- True
+-
+- False
+- False
+- GTK_JUSTIFY_LEFT
+- False
+- False
+- 0.5
+- 0.5
+- 0
+- 0
+- PANGO_ELLIPSIZE_NONE
+- -1
+- False
+- 0
+-
+-
+- 0
+- True
+- True
+-
++
+
+
+
+@@ -778,10 +721,6 @@
+
+
+
+-
+-
+-
+-
+
+
+
+diff -up gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.h.multistack gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.h
+--- gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.h.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-login-window.h 2009-09-23 18:46:06.731651392 -0400
+@@ -23,6 +23,9 @@
+ #define __GDM_GREETER_LOGIN_WINDOW_H
+
+ #include
++#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.28.0/gui/simple-greeter/gdm-greeter-plugin.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-plugin.c 2009-09-23 18:46:06.732650193 -0400
+@@ -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
++ */
++
++#include
++
++#include
++#include
++#include
++#include
++
++#include
++#include
++
++#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.28.0/gui/simple-greeter/gdm-greeter-plugin.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-plugin.h 2009-09-23 18:46:06.734650587 -0400
+@@ -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
++#include
++
++#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.28.0/gui/simple-greeter/gdm-greeter-session.c.multistack gdm-2.28.0/gui/simple-greeter/gdm-greeter-session.c
+--- gdm-2.28.0/gui/simple-greeter/gdm-greeter-session.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-greeter-session.c 2009-09-23 18:46:06.735651133 -0400
+@@ -39,6 +39,8 @@
+ #include "gdm-greeter-login-window.h"
+ #include "gdm-user-chooser-widget.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))
+@@ -46,9 +48,11 @@
+ struct GdmGreeterSessionPrivate
+ {
+ GdmGreeterClient *client;
++ GdmPluginManager *plugin_manager;
+
+ GtkWidget *login_window;
+ GtkWidget *panel;
++
+ };
+
+ enum {
+@@ -65,31 +69,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
+@@ -164,33 +183,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)
+@@ -201,26 +231,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);
+ }
+
+@@ -281,9 +317,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
+@@ -381,7 +418,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),
+@@ -546,6 +586,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);
+@@ -574,6 +672,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);
+@@ -608,6 +710,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.28.0/gui/simple-greeter/gdm-plugin-manager.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-plugin-manager.c 2009-09-23 18:46:06.737661724 -0400
+@@ -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
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++
++#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.28.0/gui/simple-greeter/gdm-plugin-manager.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-plugin-manager.h 2009-09-23 18:46:06.739650664 -0400
+@@ -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
++ *
++ */
++
++#ifndef __GDM_PLUGIN_MANAGER_H
++#define __GDM_PLUGIN_MANAGER_H
++
++#include
++
++#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.28.0/gui/simple-greeter/gdm-task-list.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-task-list.c 2009-09-23 18:46:06.849405831 -0400
+@@ -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
++ */
++
++#include "config.h"
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include
++#include
++#include
++
++#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.28.0/gui/simple-greeter/gdm-task-list.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-task-list.h 2009-09-23 18:46:06.742650418 -0400
+@@ -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
++ */
++
++#ifndef __GDM_TASK_LIST_H
++#define __GDM_TASK_LIST_H
++
++#include
++#include
++#include
++
++#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.28.0/gui/simple-greeter/gdm-user-chooser-widget.c.multistack gdm-2.28.0/gui/simple-greeter/gdm-user-chooser-widget.c
+--- gdm-2.28.0/gui/simple-greeter/gdm-user-chooser-widget.c.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/gdm-user-chooser-widget.c 2009-09-23 18:46:06.839395619 -0400
+@@ -267,9 +267,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
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.c 2009-09-23 18:46:06.866396190 -0400
+@@ -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
++ *
++ */
++
++#include
++#include
++
++#include
++
++#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.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-conversation.h 2009-09-23 18:46:06.867400089 -0400
+@@ -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
++ */
++
++
++#ifndef __GDM_CONVERSATION_H
++#define __GDM_CONVERSATION_H
++
++#include
++#include
++
++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.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.c 2009-09-23 18:46:06.748650972 -0400
+@@ -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
++ *
++ */
++
++#include
++#include
++
++#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.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-greeter-extension.h 2009-09-23 18:46:06.749650540 -0400
+@@ -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
++
++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.28.0/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdmsimplegreeter.pc.in 2009-09-23 18:46:06.752658465 -0400
+@@ -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.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-task.c 2009-09-23 18:46:06.829395324 -0400
+@@ -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
++ *
++ */
++
++#include
++#include
++
++#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.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/gdm-task.h 2009-09-23 18:46:06.830395102 -0400
+@@ -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
++ */
++
++
++#ifndef __GDM_TASK_H
++#define __GDM_TASK_H
++
++#include
++#include
++
++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.28.0/gui/simple-greeter/libgdmsimplegreeter/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/libgdmsimplegreeter/Makefile.am 2009-09-23 18:46:06.865392711 -0400
+@@ -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.28.0/gui/simple-greeter/Makefile.am.multistack gdm-2.28.0/gui/simple-greeter/Makefile.am
+--- gdm-2.28.0/gui/simple-greeter/Makefile.am.multistack 2009-09-21 16:05:27.000000000 -0400
++++ gdm-2.28.0/gui/simple-greeter/Makefile.am 2009-09-23 18:46:06.719650843 -0400
+@@ -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)"\" \
+ -DGDM_CACHE_DIR=\""$(localstatedir)/cache/gdm"\" \
++ -DGDM_SIMPLE_GREETER_PLUGINS_DIR="\"$(GDM_SIMPLE_GREETER_PLUGINS_DIR)\""\
+ -DAT_SPI_REGISTRYD_DIR="\"$(AT_SPI_REGISTRYD_DIR)\"" \
+ $(DISABLE_DEPRECATED_CFLAGS) \
+ $(GTK_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) \
+@@ -137,6 +148,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) \
+@@ -307,18 +319,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.28.0/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.c 2009-09-23 18:46:06.840395606 -0400
+@@ -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
++ *
++ */
++
++#include
++#include "gdm-fingerprint-extension.h"
++#include "gdm-conversation.h"
++#include "gdm-task.h"
++
++#include
++#include
++#include
++
++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 ("gdm-fingerprint");
++ create_page (extension);
++ create_actions (extension);
++ gdm_fingerprint_extension_reset (GDM_CONVERSATION (extension));
++}
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint-extension.h 2009-09-23 18:46:06.789648576 -0400
+@@ -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
++ */
++
++#ifndef __GDM_FINGERPRINT_EXTENSION_H
++#define __GDM_FINGERPRINT_EXTENSION_H
++
++#include
++#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.28.0/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint.pam
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/gdm-fingerprint.pam 2009-09-23 18:46:06.791416748 -0400
+@@ -0,0 +1,17 @@
++# Sample PAM file for doing fingerprint authentication.
++# Distros should replace this with what makes sense for them.
++auth required pam_env.so
++auth required pam_fprintd.so
++auth sufficient pam_succeed_if.so uid >= 500 quiet
++auth required pam_deny.so
++
++account required pam_unix.so
++account sufficient pam_localuser.so
++account sufficient pam_succeed_if.so uid < 500 quiet
++account required pam_permit.so
++
++password required pam_deny.so
++
++session optional pam_keyinit.so revoke
++session required pam_limits.so
++session required pam_unix.so
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/icons/16x16/Makefile.am 2009-09-23 18:46:06.793390394 -0400
+@@ -0,0 +1,5 @@
++iconsdir = $(datadir)/icons/hicolor/16x16/apps
++
++icons_DATA = gdm-fingerprint.png
++
++EXTRA_DIST = $(icons_DATA)
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/icons/48x48/Makefile.am 2009-09-23 18:46:06.795405874 -0400
+@@ -0,0 +1,5 @@
++iconsdir = $(datadir)/icons/hicolor/48x48/apps
++
++icons_DATA = gdm-fingerprint.png
++
++EXTRA_DIST = $(icons_DATA)
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/icons/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/icons/Makefile.am 2009-09-23 18:46:06.796402997 -0400
+@@ -0,0 +1 @@
++SUBDIRS = 16x16 48x48
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/Makefile.am 2009-09-23 18:46:06.783657869 -0400
+@@ -0,0 +1,56 @@
++SUBDIRS = icons
++
++NULL =
++PAM_SERVICE_NAME = gdm-fingerprint
++
++extensiondir = $(extensionsdatadir)/fingerprint
++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=\""$(PAM_SERVICE_NAME)"\" \
++ -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
++
++$(PAM_SERVICE_NAME): $(PAM_SERVICE_NAME).pam
++ cp $(PAM_SERVICE_NAME).pam $(PAM_SERVICE_NAME)
++
++pamdir = $(PAM_PREFIX)/pam.d
++pam_DATA = $(PAM_SERVICE_NAME)
++
++EXTRA_DIST = $(extension_DATA) $(PAM_SERVICE_NAME).pam
++CLEANFILES = $(PAM_SERVICE_NAME)
++
++MAINTAINERCLEANFILES = \
++ *~ \
++ $(PAM_SERVICE_NAME) \
++ Makefile.in
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/page.ui
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/page.ui 2009-09-23 18:46:06.797402706 -0400
+@@ -0,0 +1,56 @@
++
++
++
++
++
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/plugin.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/fingerprint/plugin.c 2009-09-23 18:46:06.798411004 -0400
+@@ -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
++ *
++ */
++
++#include "gdm-fingerprint-extension.h"
++
++#include
++#include
++
++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.28.0/gui/simple-greeter/plugins/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/Makefile.am 2009-09-23 18:46:06.801415786 -0400
+@@ -0,0 +1 @@
++SUBDIRS = password fingerprint smartcard
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/password/gdm-password-extension.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/password/gdm-password-extension.c 2009-09-23 18:46:06.832406042 -0400
+@@ -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
++ *
++ */
++
++#include
++#include "gdm-password-extension.h"
++#include "gdm-conversation.h"
++#include "gdm-task.h"
++
++#include
++#include
++#include
++
++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.28.0/gui/simple-greeter/plugins/password/gdm-password-extension.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/password/gdm-password-extension.h 2009-09-23 18:46:06.758650778 -0400
+@@ -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
++ */
++
++#ifndef __GDM_PASSWORD_EXTENSION_H
++#define __GDM_PASSWORD_EXTENSION_H
++
++#include
++#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.28.0/gui/simple-greeter/plugins/password/gdm-password.pam
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/password/gdm-password.pam 2009-09-23 18:46:06.759650276 -0400
+@@ -0,0 +1,19 @@
++# Sample PAM file for doing password authentication.
++# Distros should replace this with what makes sense for them.
++auth required pam_env.so
++auth sufficient pam_unix.so nullok try_first_pass
++auth requisite pam_succeed_if.so uid >= 500 quiet
++auth required pam_deny.so
++
++account required pam_unix.so
++account sufficient pam_localuser.so
++account sufficient pam_succeed_if.so uid < 500 quiet
++account required pam_permit.so
++
++password requisite pam_cracklib.so try_first_pass retry=3 type=
++password sufficient pam_unix.so nullok try_first_pass use_authtok
++password required pam_deny.so
++
++session optional pam_keyinit.so revoke
++session required pam_limits.so
++session required pam_unix.so
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/password/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/password/Makefile.am 2009-09-23 18:46:06.755661360 -0400
+@@ -0,0 +1,53 @@
++NULL =
++PAM_SERVICE_NAME = gdm-password
++
++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=\""$(PAM_SERVICE_NAME)"\" \
++ -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
++
++$(PAM_SERVICE_NAME): $(PAM_SERVICE_NAME).pam
++ cp $(PAM_SERVICE_NAME).pam $(PAM_SERVICE_NAME)
++
++pamdir = $(PAM_PREFIX)/pam.d
++pam_DATA = $(PAM_SERVICE_NAME)
++
++EXTRA_DIST = $(extension_DATA) $(PAM_SERVICE_NAME).pam
++CLEANFILES = $(PAM_SERVICE_NAME)
++
++MAINTAINERCLEANFILES = \
++ *~ \
++ $(PAM_SERVICE_NAME) \
++ Makefile.in
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/password/page.ui
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/password/page.ui 2009-09-23 18:46:06.760651730 -0400
+@@ -0,0 +1,56 @@
++
++
++
++
++ True
++ vertical
++
++
++ True
++
++
++ True
++
++
++ False
++ False
++ 0
++
++
++
++
++ True
++ True
++ True
++
++
++ 1
++
++
++
++
++ True
++ True
++ 0
++
++
++
++
++ True
++
++
++ True
++
++
++ 0
++
++
++
++
++ True
++ True
++ 1
++
++
++
++
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/password/plugin.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/password/plugin.c 2009-09-23 18:46:06.762655966 -0400
+@@ -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
++ *
++ */
++
++#include "gdm-password-extension.h"
++
++#include
++#include
++
++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.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard.c 2009-09-23 18:46:06.814407453 -0400
+@@ -0,0 +1,558 @@
++/* gdm-smartcard.c - smartcard object
++ *
++ * 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.
++ */
++#define GDM_SMARTCARD_ENABLE_INTERNAL_API
++#include "gdm-smartcard.h"
++
++#include
++#include
++#include
++
++#include
++#include
++
++#include
++#include
++#include
++#include
++#include
++#include
++
++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.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.c 2009-09-23 18:46:06.868400496 -0400
+@@ -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
++ *
++ */
++
++#include
++#include "gdm-smartcard-extension.h"
++#include "gdm-conversation.h"
++#include "gdm-task.h"
++
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include
++#include
++
++#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 ("gdm-smartcard");
++ create_page (extension);
++ create_actions (extension);
++ gdm_smartcard_extension_reset (GDM_CONVERSATION (extension));
++}
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-extension.h 2009-09-23 18:46:06.806407203 -0400
+@@ -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
++ */
++
++#ifndef __GDM_SMARTCARD_EXTENSION_H
++#define __GDM_SMARTCARD_EXTENSION_H
++
++#include
++#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.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard.h 2009-09-23 18:46:06.816405822 -0400
+@@ -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
++#include
++
++#include
++
++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.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.c 2009-09-23 18:46:06.809405560 -0400
+@@ -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
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++
++#include
++#include
++
++#include
++#include
++#include
++#include
++#include
++
++#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
++
++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.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-manager.h 2009-09-23 18:46:06.811390170 -0400
+@@ -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
++#include
++
++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.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard.pam
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard.pam 2009-09-23 18:46:06.817400851 -0400
+@@ -0,0 +1,18 @@
++# Sample PAM file for doing smartcard authentication.
++# Distros should replace this with what makes sense for them.
++auth required pam_env.so
++auth [success=done ignore=ignore default=die] pam_pkcs11.so wait_for_card
++auth requisite pam_succeed_if.so uid >= 500 quiet
++auth required pam_deny.so
++
++account required pam_unix.so
++account sufficient pam_localuser.so
++account sufficient pam_succeed_if.so uid < 500 quiet
++account required pam_permit.so
++
++password optional pam_pkcs11.so
++password requisite pam_cracklib.so try_first_pass retry=3 type=
++
++session optional pam_keyinit.so revoke
++session required pam_limits.so
++session required pam_unix.so
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/gdm-smartcard-worker.c 2009-09-23 18:46:06.812410900 -0400
+@@ -0,0 +1,167 @@
++#include "config.h"
++
++#include
++#include
++#include
++#include
++
++#include
++
++#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'? "" : 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.28.0/gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/icons/16x16/Makefile.am 2009-09-23 18:46:06.819416331 -0400
+@@ -0,0 +1,5 @@
++iconsdir = $(datadir)/icons/hicolor/16x16/apps
++
++icons_DATA = gdm-smartcard.png
++
++EXTRA_DIST = $(icons_DATA)
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/smartcard/icons/48x48/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/icons/48x48/Makefile.am 2009-09-23 18:46:06.821400871 -0400
+@@ -0,0 +1,5 @@
++iconsdir = $(datadir)/icons/hicolor/48x48/apps
++
++icons_DATA = gdm-smartcard.png
++
++EXTRA_DIST = $(icons_DATA)
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/smartcard/icons/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/icons/Makefile.am 2009-09-23 18:46:06.822408960 -0400
+@@ -0,0 +1 @@
++SUBDIRS = 16x16 48x48
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/smartcard/Makefile.am
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/Makefile.am 2009-09-23 18:46:06.803405565 -0400
+@@ -0,0 +1,77 @@
++SUBDIRS = icons
++
++NULL =
++PAM_SERVICE_NAME = gdm-smartcard
++
++extensiondir = $(extensionsdatadir)/smartcard
++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=\""$(pam_DATA)"\" \
++ -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)
++
++$(PAM_SERVICE_NAME): $(PAM_SERVICE_NAME).pam
++ cp $(PAM_SERVICE_NAME).pam $(PAM_SERVICE_NAME)
++
++pamdir = $(PAM_PREFIX)/pam.d
++pam_DATA = $(PAM_SERVICE_NAME)
++
++EXTRA_DIST = $(extension_DATA) $(PAM_SERVICE_NAME).pam
++CLEANFILES = $(PAM_SERVICE_NAME)
++
++MAINTAINERCLEANFILES = \
++ *~ \
++ $(PAM_SERVICE_NAME) \
++ Makefile.in
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/smartcard/page.ui
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/page.ui 2009-09-23 18:46:06.823391278 -0400
+@@ -0,0 +1,56 @@
++
++
++
++
++ True
++ vertical
++
++
++ True
++
++
++ True
++
++
++ False
++ False
++ 0
++
++
++
++
++ True
++ True
++ True
++
++
++ 1
++
++
++
++
++ True
++ True
++ 0
++
++
++
++
++ True
++
++
++ True
++
++
++ 0
++
++
++
++
++ True
++ True
++ 1
++
++
++
++
+diff -up /dev/null gdm-2.28.0/gui/simple-greeter/plugins/smartcard/plugin.c
+--- /dev/null 2009-09-23 15:09:11.116404522 -0400
++++ gdm-2.28.0/gui/simple-greeter/plugins/smartcard/plugin.c 2009-09-23 18:46:06.825390205 -0400
+@@ -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
++ *
++ */
++
++#include "gdm-smartcard-extension.h"
++
++#include
++#include
++
++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);
++}
diff --git a/gdm.spec b/gdm.spec
index 4bfa274..40e090a 100644
--- a/gdm.spec
+++ b/gdm.spec
@@ -98,7 +98,7 @@ Patch3: gdm-2.23.92-save-root-window.patch
# should probably be changed to get the system layout from the X server
Patch13: gdm-system-keyboard.patch
-Patch19: gdm-2.27.4-multistack.patch
+Patch19: gdm-multistack.patch
# Fedora-specific
Patch99: gdm-2.23.1-fedora-logo.patch